Official
C - 街灯と吹雪 / Streetlights and Blizzard Editorial
by
C - 街灯と吹雪 / Streetlights and Blizzard Editorial
by
physics0523
Editorial for printing
この問題は以下の手順で解くことができます。
- まず、各街灯が負うダメージの総和 \(S_i\) を求める。
- その後、 \(H_i-S_i > 0\) かどうかで各街灯が最終的に生き残るかどうかを判定する。
ダメージの与えられ方は、街灯の区間 \(L_i,L_{i+1},\dots,R_i\) に対して \(D_i\) という形をしています。この形のダメージがたくさん与えられます。
このダメージを取り扱うにはどうすればよいでしょうか?
結局、数列のある区間にある値を加算するというクエリをたくさんまとめて処理出来ればよいです。これは有名な問題です。
今回は imos法 と呼ばれる方法を用います。この方法を今回の問題に沿った形で説明します。
- 「 \(A_{L_i},A_{L_i+1},\dots,A_{R_i}\) に \(D_i\) を加算する」というクエリが沢山あり、これらをまとめて処理したい。
- まず、ひとつの加算を以下の形に変換する。
- \(A_{L_i}\) に \(D_i\) 加算する。
- \(A_{R_i+1}\) から \(D_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+Q)\) です。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main(){
ll N,M;
cin >> N >> M;
vector<ll> H(N+5);
for(ll i=1;i<=N;i++){ cin >> H[i]; }
vector<ll> ds(N+5,0);
while(M--){
ll L,R,D;
cin >> L >> R >> D;
ds[L]+=D;
ds[R+1]-=D;
}
ll res=0;
for(ll i=1;i<=N;i++){
ds[i]+=ds[i-1];
if(H[i]-ds[i]>0){res++;}
}
cout << res << "\n";
return 0;
}
posted:
last update:
