公式

C - 水やり / Watering 解説 by physics0523


\(Q\) 回の水やりの後の水分量を問われているため、 \(Q\) 回の操作をそれぞれ行う必要はなく、操作をまとめて行ってその結果を求めればよいです。

もし \(Q=1\) であれば、頂点 \(x\) の部分木に \(d\) 加算するのは以下の要領で行えばよいです。

  • 最初、全ての頂点に \(0\) が書かれているとする。
  • 予め、頂点 \(x\)\(d\) 加算する。(★)
  • その後、根から順に以下の操作を行う。(この問題の制約で \(P_i < i\) なので、実際には頂点 \(1,2,\dots,N\) の番号順に操作を行えばよいです)
    • 頂点 \(x\) に書き込まれている値を、頂点 \(x\) の(直接の)子すべてに加算する。

こうすると、頂点 \(x\) の部分木全体に \(d\) 加算されたことが分かります。

\(Q>1\) 、つまり加算が複数回発生してもやることはほとんど変わりません。
★ の部分を全ての加算について予め行っておくことで、部分木に対する加算を全部まとめて処理できます。

時間計算量は \(O(N+Q)\) です。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

int main(){
  ll N,Q;
  cin >> N >> Q;
  vector<vector<ll>> chi(N);
  for(ll i=1;i<N;i++){
    ll P;
    cin >> P;
    chi[P-1].push_back(i);
  }
  vector<ll> res(N,0);
  while(Q--){
    ll X,D;
    cin >> X >> D;
    res[X-1]+=D;
  }
  for(ll i=0;i<N;i++){
    if(i){cout << " ";}
    cout << res[i];
    for(auto &nx : chi[i]){
      res[nx]+=res[i];
    }
  }cout << "\n";
  return 0;
}

投稿日時:
最終更新: