[AGC057] B - 2A + x

[Q] https://atcoder.jp/contests/agc057/tasks/agc057_b


0. 統合する。(しなくてもいい)
5 10 20 は、5は10とかぶるし、10は20とかぶるから、5と10は消去していい。
これは問題を少し単純にするだけの処理。

最大値の20を動かした消去判定を、してはいけない。
値が大きくなればなるほど、max-minは大きくなってしまう。

1. 各値について、2つの変動値をメモする。
10 30 50 70だとして。現時点での最大値70をtargetとする。
(a) *2+Xして、70以下の最大値をメモ。
(b) *2して、70以上の最小値をメモ。
2. (b)の値でN通りシミュレートする。
一番いいスコアが解答。

Q. (b) 2倍するだけの値が、どうしてmax値とみなしたときの最小とわかるの?
途中で*2+Xをはさんだ結果、よりtargetの70に近い70以上の値になることはないの?
A. なさそう。
もし近づくとしたら、それは0の統合処理に吸収されるべき。

実装

ll N, X;
ll A[100010];

int main(){
 cincout();
 
 cin >> N >> X;
 rep(i, N) cin >> A[i];
 set<ll> S;
 rep(i, N){
   S.insert(A[i]);
 }
 
 // まず重複している数字を統合する。5 10 20 があった場合、5 10 は 20 に吸収。
 // これは省略してもいい。
 for(auto s: S){
   ll low = s*2;
   ll high = s*2+X;
   auto it = S.lower_bound(low);
   while (it != S.end()){
     if (*it <= high){
       // low ~ highの間に遷移できるなら吸収。
       S.erase(S.find(s));
       break;
     }
     low *= 2;
     high = high*2 + X;
     it = S.lower_bound(low);
   }
 }

 vector<pll> V; // high, low
 V.reserve((ll)S.size());
 for(auto s: S){
   V.emplace_back(s, s);
 }
 ll vsz = V.size();
 // targetをこえないhigh(毎回*2+Xする場合)と, targetを超えるlow(毎回2倍するだけ)をメモする。
 ll target = V[vsz-1].first;
 rep(i, vsz-1){ // こえないhigh, こえるlowに変更
   auto &[high, low] = V[i];
   while (low * 2 < target){
     low *= 2;
     high = high * 2 + X; // targetをこえないぎりぎりのhigh
   }
   low *= 2; // targetをこえる最小のlow
 }
 // low(target)をN通り置換する全探索。
 sort(all(V));
 // highだけでみると、最もtargetに寄った値に変換されているはず。
 // V[].first = a, b, c, target(, a*2, b*2...)
                ~~~~~~~~~~~~~~~
 ll ans = oo;
 rep(i, vsz){ // max値(low)を全探
   auto[high, low] = V[i];
   chmin(ans, target - high);
   chmax(target, low); // targetを変更。今検証した値が、既存のtargetを超える最小値に設定。
 }

 if (ans < X) ans = 0; // Xにゆとりがあれば絶対0にできる
 cout << ans << endl;
}

https://atcoder.jp/contests/agc057/submissions/31498614

いいなと思ったら応援しよう!