公式

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


最初全ての \(C_i=0\) だと思って問題を解いたうえで、初期状態の \(C\) は出力の際に足せばよいです。

よって、本質的には 「 \(A_{L_i},A_{L_i+1},\dots,A_{R_i}\)\(K\) を加算する」という処理をまとめて(オフラインで)処理できればこの問題を解くことができます。

imos法 と呼ばれる方法を用います。この方法を今回の問題に沿った形で説明します。

  • \(A_{L_i},A_{L_i+1},\dots,A_{R_i}\)\(K\) を加算する」というクエリが沢山あり、これらをまとめて処理したい。
  • まず、ひとつの加算を以下の形に変換する。
    • \(A_{L_i}\)\(K\) 加算する。
    • \(A_{R_i+1}\) から \(K\) 減算する。
  • 全ての加算の処理を終えた後、以下の通りに累積和を取る処理を行う。
    • \(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+Q)\) です。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

int main(){
  ll N,K,Q;
  cin >> N >> K >> Q;
  vector<ll> C(N);
  for(auto &nx : C){cin >> nx;}
  vector<ll> arr(N+1);
  while(Q--){
    ll L,R;
    cin >> L >> R;
    arr[L-1]++;
    arr[R]--;
  }
  for(ll i=0;i<N;i++){
    if(i){
      cout << " ";
      arr[i]+=arr[i-1];
    }
    ll ans=arr[i]*K+C[i];
    cout << ans;
  }cout << "\n";
  return 0;
}

投稿日時:
最終更新: