公式

C - 花壇の水やり / Watering the Flower Bed 解説 by physics0523


数列のうちある区間 \([L_i,R_i]\) に \(W_i\) を加算するという行為をまとめて高速に処理できればよいです。
そこで、 imos法 と呼ばれる方法を用います。この方法を今回の問題に沿った形で説明します。

  • 「 \(A_{L_i},A_{L_i+1},\dots,A_{R_i}\) に \(W_i\) を加算する」というクエリが沢山あり、これらをまとめて処理したい。
  • まず、ひとつの加算を以下の形に変換する。
    • \(A_{L_i}\) に \(W_i\) 加算する。
    • \(A_{R_i+1}\) から \(W_i\) 減算する。
  • 全ての加算の処理を終えた後、以下の通りに累積和を取る処理を行う。
    • \(A_{i+1}\) に \(A_i\) を加算することを、 \(i\) の昇順に繰り返す。

このようにすることで、変換した後の加算が累積和を取った後に区間加算に対応していることがわかります。

理解の助けの為、具体例を示します。

  • \(A_2,A_3,A_4\) に \(1\) を加算したいとする。
    • \(A_2\) に \(1\) 加算、 \(A_5\) から \(1\) 減算と言い換えられる。この時点で \(A=(0,1,0,0,-1,0,\dots)\) です。
  • \(A_3,A_4,A_5\) に \(10\) を加算したいとする。
    • \(A_3\) に \(10\) 加算、 \(A_6\) から \(10\) 減算と言い換えられる。この時点で \(A=(0,1,10,0,-1,-10,\dots)\) です。
  • これで全ての加算が完了したとする。
    • \(A\) の累積和を取る。 \(A=(0,1,11,11,10,0,\dots)\) となり、所望の加算が実現していることが分かる。

これで各花壇に与えられた水の量が判明したので、あとは各花壇について根腐れを起こすかを判定すればよいです。

全体の時間計算量は \(O(N+M)\) です。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

int main(){
  ll N,M;
  cin >> N >> M;
  vector<ll> S(N);
  for(auto &nx : S){cin >> nx;}
  vector<ll> T(N+1,0);
  for(ll i=0;i<M;i++){
    ll L,R,W;
    cin >> L >> R >> W;
    T[L-1]+=W;
    T[R]-=W;
  }

  ll res=0;
  for(ll i=0;i<N;i++){
    if(S[i]<T[i]){res++;}
    T[i+1]+=T[i];
  }
  cout << res << "\n";
  return 0;
}

投稿日時:
最終更新: