ABC119 D Lazy Faith を二分探索を使わずに解く(C++)
irisruneです。一昨日のAGCは所用で参加できなかったので今日も過去問になります。
前回と同様、今回もC++での提出コードです。
https://atcoder.jp/contests/abc119/tasks/abc119_d
この問題のポイントは
に(東西それぞれで)最寄りの神社と寺を高速で求める方法
- 最寄りの神社と寺から要求された解を求める方法
…らしいです1。自分は完全に1.で引っかかった口でした。
回答と方針
割と思いつきやすい二分探索が模範解答だったわけですが、自分の回答はというと…
#include <bits/stdc++.h>
using namespace std;
int a, b, q;
vector<long long> va;
vector<long long> vb;
int idxA, idxB;
class X {
public : int idx;
public : long long x;
public : long long l;
X(int i, long long s){
idx = i;
x = s;
}
};
vector<X> vX;
bool cmp_idx(const X &a, const X &b){
return a.idx < b.idx;
}
bool cmp_x(const X &a, const X &b){
return a.x < b.x;
}
void init() {
cin >> a >> b >> q;
for(int i = 0; i < a; i++){
long long s;
cin >> s;
va.push_back(s);
}
for(int i = 0; i < b; i++){
long long s;
cin >> s;
vb.push_back(s);
}
for(int i = 0; i < q; i++){
long long x;
cin >> x;
X X(i, x);
vX.push_back(X);
}
sort(vX.begin(), vX.end(), cmp_x);
}
long long solve(long long x){
for(int i = idxA; i < a; i++){
if(x < va.at(i)) break;
idxA++;
}
for(int i = idxB; i < b; i++){
if(x < vb.at(i)) break;
idxB++;
}
long long westA, eastA, westB, eastB;
if(idxA == 0) westA = -50000000000;
else westA = va.at(idxA - 1);
if(idxA == a) eastA = 50000000000;
else eastA = va.at(idxA);
if(idxB == 0) westB = -50000000000;
else westB = vb.at(idxB - 1);
if(idxB == b) eastB = 50000000000;
else eastB = vb.at(idxB);
long long minL = max(x - westA, x - westB);
minL = min(minL, (eastA - x) + (eastA - westB));
minL = min(minL, (x - westB) + (eastA - westB));
minL = min(minL, (eastB - x) + (eastB - westA));
minL = min(minL, (x - westA) + (eastB - westA));
minL = min(minL, max(eastA - x, eastB - x));
return minL;
}
int main(void)
{
cin.tie(0);
ios::sync_with_stdio(false);
init();
for(int i = 0; i < q; i++){
vX.at(i).l = solve(vX.at(i).x);
}
sort(vX.begin(), vX.end(), cmp_idx);
for(int i = 0; i < q; i++){
cout << vX.at(i).l << "\n";
}
return 0;
}
一応方針を説明すると、
を保持しながら
を西から順にソートする。
の西最寄りの神社と寺を記録する変数をおく。
- 2.の変数に記録しながらソートした順に
について問題を解く。
を
でソートしてから出力する。
という感じです。
こんな手順でなぜ解けるか
単純に線形探索をする →各
につき
の計算量がかかる →全体で
になってしまう →TLEしてしまう
を西から順番に見て線形探索をする →
について計算量の合計が
になる →TLEしない
という形になります。前回のABC119-Cといい全体の計算量を抑える手法をよく使いますね、今回は明らかに嘘解法ですが。
ちなみにソート自体にかかるので二分探索を使う手法と計算量は(オーダーの上では)同じです。
が予めソートされているならもっと速いですね。
コードレビュー
- グローバル変数の濫用。
- INFがマジックナンバーに見えるので変数(定数)を使った方がよい。
雑記
- Kotlinのコードに比べてC++のコードが随分長い気がするのは気のせいでしょうか。
- Kotlinで二分探索を用いて解いてみましたが、入力にreadLine()を用いても1400msくらいに落ち着きました。