[2019.2.28]BZOJ4033 [HAOI2015]樹上染色
阿新 • • 發佈:2019-03-17
val 表示 mat sca ace fine pan span 節點
首先我們設\(dp_{i,j}\)表示\(i\)和的子樹中,有\(j\)個黑色節點的最大邊權和。
我們設\(i\)當前已合並的子樹大小為\(sz_i\)。
現在我們要合並節點\(x\)和它的子節點\(y\)。
我們考慮\(x\)和\(y\)之間的邊對答案的貢獻。
這個貢獻就是這條邊[(一側的黑點數\(\times\)另一側的黑點數)+(一側的白點數\(\times\)另一側的白點數)]\(\times\)邊權。
為什麽呢?
顯然對於任意位於這條邊兩側的同色點,它們之間的路徑必然經過這條邊。
我們設這次合並之前的\(dp_x\)為\(ldp\),這條邊邊權為\(ev\),那麽有
\(dp_{x,i}=max\{dp_{y,j}+ldp_{i-j}+j\times(k-j)\times ev+(sz_y-j)\times(n-k-sz_y+j)\times ev\}\)
但是這樣看起來單次合並是\(O(n^2)\)的,總時間復雜度就是\(O(n^3)\)的。
一開始我也是這麽認為的。
一交發現過了,而且跑的很快。
它實際上似乎是\(O(n^2)\)的。
為什麽呢?
樹形dp復雜度太玄學了
code:
#include<bits/stdc++.h> #define Add(l,r,val) (c[l]+=val,c[r+1]-=val) #define Sum(l,r) (sum[r]-(l?sum[l-1]:0)) using namespace std; struct edge{ int t,v,nxt; }e[4010]; int n,k,u,v,w,cnt,be[2010],sz[2010]; long long dp[2010][2010],cpy[2010],ans; void add(int x,int y,int val){ e[++cnt].t=y,e[cnt].v=val,e[cnt].nxt=be[x],be[x]=cnt; } void Merge(int x,int y,int ev){ for(int i=0;i<=k;++i)cpy[i]=dp[x][i],dp[x][i]=0; for(int i=0;i<=k&&i<=sz[y];++i)for(int j=0;i+j<=k&&j<=sz[x];++j)dp[x][i+j]=max(dp[x][i+j],dp[y][i]+cpy[j]+1ll*ev*i*(k-i)+1ll*ev*(sz[y]-i)*(n-k-sz[y]+i)); } void TDP(int x){ sz[x]=1; for(int i=be[x];i;i=e[i].nxt)!sz[e[i].t]?TDP(e[i].t),Merge(x,e[i].t,e[i].v),sz[x]+=sz[e[i].t],0:0; } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<n;++i)scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w); TDP(1); printf("%lld",dp[1][k]); return 0; }
[2019.2.28]BZOJ4033 [HAOI2015]樹上染色