C - 周期的な追いかけっこ / Periodic Chase Editorial
by
physics0523
\(NQ \le 10^7\) という制約があるため、ひとつのクエリに時間計算量 \(O(N)\) 程度かけて良いという見立てで解くと良いでしょう。
まず、時刻 \(0\) から時刻 \(N\) に到達するまでをシミュレーションします。この段階で追いついた場合はそれを答えとします。
以降、時刻 \(N\) までに追いつかなかったとします。
シミュレーションの過程での最小の距離を \(m\) とします。また、時刻 \(0\) 時点での距離を \(d\) 、時刻 \(N\) 時点での距離を \(d'\) とします。
\(d \le d'\) であれば時刻 \(N\) 以降でも追いつくことはありません。
\(d > d'\) であれば周期ごとに距離は縮まっているので、いつかは追い付きます。
さらに、 \(k\) 周期目では \(1\) 周期目に比べて距離が \((k-1) \times (d-d')\) 縮んだ状態であることも分かります。
ここで、 \(l = \lceil m/(d-d') \rceil\) 周期経過した時を考えましょう。
\(l\) 周期目での最小の距離は \(m-(l-1) \times (d-d')\) となります。これは \(0\) より大きいです。
\(l+1\) 周期目での最小の距離は \(m-l \times (d-d')\) となります。これは \(0\) 以下です。
よって、距離を \(l \times (d-d')\) 縮めた状態から \(l+1\) 周期目をシミュレーションすれば距離が \(0\) となる最小の時間が判明します。
全体で時間計算量 \(O(NQ)\) でこの問題を解くことができました。
Bonus! よりよい時間計算量でこの問題を解いてみましょう。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main(){
ll N,Q;
cin >> N >> Q;
vector<ll> A(N),B(N);
for(ll i=0;i<N;i++){ cin >> A[i] >> B[i]; }
while(Q--){
ll typ;
cin >> typ;
if(typ==1){
ll i,a,b;
cin >> i >> a >> b;
i--;
A[i]=a;
B[i]=b;
}
else{
ll d;
cin >> d;
ll cd=d,res=-1,low=cd;
for(ll i=0;i<N;i++){
cd+=A[i];
cd-=B[i];
if(cd<=0){res=i+1; break;}
low=min(low,cd);
}
if(res>=0){
cout << res << "\n";
continue;
}
if(d<=cd){
cout << "-1\n";
continue;
}
ll dec=(d-cd);
ll cyc=(low+dec-1)/dec;
res=cyc*N;
cd=d-cyc*dec;
for(ll i=0;;i++){
cd+=A[i%N];
cd-=B[i%N];
if(cd<=0){
cout << res+i+1 << "\n";
break;
}
}
}
}
return 0;
}
posted:
last update:
