Official

C - 区間加算 / Range Addition Editorial by physics0523


B 問題を先に解く・先に解説を読むことを前提として説明します。

B 問題では累積和を使って区間の和を求める問題を解きました。これを「逆方向」に使ってこの問題を解くことができないでしょうか?


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

  • \(A_{L_i},A_{L_i+1},\dots,A_{R_i}\)\(X_i\) を加算する」というクエリが沢山あり、これらをまとめて処理したい。
  • まず、ひとつの加算を以下の形に変換する。
    • \(A_{L_i}\)\(X_i\) 加算する。
    • \(A_{R_i+1}\) から \(X_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> A(N+5,0);
  while(M--){
    ll L,R;
    cin >> L >> R;
    A[L]++;
    A[R+1]--;
  }
  for(ll i=2;i<=N;i++){
    A[i]+=A[i-1];
  }
  for(ll i=1;i<=N;i++){
    if(i>=2){cout << " ";}
    cout << A[i];
  }cout << "\n";
  return 0;
}

posted:
last update: