[JOI精進録] 難易度8「安全点検」(JOI2020/2021 二次予選D)
難易度8初ACです。
問題リンク
問題概要
人の人がいる。彼らは1分間で以下の行動を1回できる。
・距離1だけ移動する
・今いる座標の施設で1回作業する
個の施設があり、座標に施設があります。
回の作業をそれぞれの施設で行うとき、すべて完了できるのは最短で何分必要ですか?
解説
最初の解法 (18/100点)
答えで二分探索をします。以降、二分探索の範囲0~1e18をで1e18とし、と表記します。
判定関数を秒で全ての作業を終えられるか、で設定します。
実装は人ごとに秒でまだ点検の完了していない一番遠い施設まで行き、できるだけ作業をします。
その施設の点検が完了してまだ秒数が残っている場合、途中の施設によって作業をしていたことにします。
計算量はです。
提出結果
グハァ pic.twitter.com/ZDli0cgMJn
— DeltaStruct @駆け出し競プロer (@DeltaStruct) September 25, 2023
TLE1で落ちている...だと...
高速化を試みる (18/100点)
累積和と二分探索で高速化を試みました。
計算量はになりました。
提出結果
(泣) https://t.co/dyK8a1T7Ly pic.twitter.com/Ekxe6LVG2B
— DeltaStruct @駆け出し競プロer (@DeltaStruct) September 25, 2023
ひどくなりました。
あることに気づく
もっと高速な判定関数って作れるのか?
— DeltaStruct @駆け出し競プロer (@DeltaStruct) September 26, 2023
O((K+N) log 1e18)でTLE1(Nは1e5以下制約なのでちょっとまってこっちの方が高速じゃんなにしてんだ
高速化の方法を間違えて計算量を悪化させていることにここでようやく気づきました。
そしてが最大1e9で一番ネックになっているのでこのをどうにかすれば良さそうです。
AC解法 (100/100点)
判定関数を工夫します。
秒で人を使って完了できるかという判定方法はとなります。
視点を変えて秒で作業を終わらせるには何人が必要か、という実装をすると施設で必要な人数は除算で求められるのでで計算できます。
計算量はとなりました。
ACコード
#include <bits/stdc++.h> using namespace std; int main(){ long long n,k; cin >> n >> k; vector<long long> A(n),B(n),acc(n+1); for (long long& a:A) cin >> a; for (long long& a:B) cin >> a; int i(1); auto vi = views::iota(0ll,(long long)1e18); cout << *lower_bound(vi.begin(),vi.end(),0ll,[&A,&B,&k,&n](const long long& x, const long long& y){ if (A.back()>=x) return true; long long i(-1),b(B.back()); for (int j(n-1);j > -1;--j){ long long u = x-A[j]; i+=ceil((double)b/(double)u); b -= ceil((double)b/(double)u)*u; if (j>0&&b<1){ while(j>0&&b<1) --j,b+=B[j]; ++j; } } return (i>=k); }) << endl; }