実装は面倒だけど方針はシンプルな問題。
https://yukicoder.me/problems/no/1524
問題
根付き木が与えられる。
各頂点vには2整数A[v]、B[v]が設定されている。
ここから、いくつかの頂点を無効化する。
この際、残された頂点について、親頂点vのA[v]より子頂点cのA[c]の方が大きくなるようにしたい。
条件を満たす範囲で、B[v]の総和の最大値を求めよ。
解法
f(v,x) := 頂点vのSubTree以下で考えるとき、x以上のA[v]を取るときのB[*]の総和の最大値
とする。
f(0,0)が解となるが、このf(v,x)を愚直に埋めるとO(N^2)かかってしまう。
g(v,x) := f(v,x-1)-f(v,x)と差分を取るようにする。
するとg(v,x)が非0となるのは、高々vのSubTreeの頂点数ぐらいである。
そこでg(v,x)をmapを使い値が非0となるところだけxとg(v,x)の値を持つように使用。
2つの子頂点c1,c2のf(c1,x)とf(c2,x)をマージしようとすると、両者の足し算とか必要で割と面倒。
しかし、g(c1,x)とg(c2,x)で考えるとマージテクで容易にくっ付けるだけで済む。
最後にg(0,*)の和を求めればよい。
int N; int P[101010]; vector<int> E[101010]; int A[101010],B[101010]; map<int,int> M[202020]; void solve() { int i,j,k,l,r,x,y; string s; cin>>N; FOR(i,N-1) { cin>>P[i+1]; E[P[i+1]-1].push_back(i+1); } FOR(i,N) cin>>A[i]; FOR(i,N) cin>>B[i]; for(i=N-1;i>=0;i--) { FORR(e,E[i]) { if(M[i].size()<M[e].size()) swap(M[i],M[e]); FORR2(a,b,M[e]) M[i][a]=b; } y=B[i]; while(y) { auto it=M[i].lower_bound(A[i]); if(it==M[i].begin()) break; it--; x=min(y,it->second); it->second-=x; y-=x; if(it->second==0) M[i].erase(it); } M[i][A[i]]=B[i]; } ll ret=0; FORR2(m,v,M[0]) ret+=v; cout<<ret<<endl; }
まとめ
マージテクはともかく、差を管理するのに気付くのはちょっと難しい。