Official

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: