1. 程式人生 > >[2019.2.28]BZOJ4033 [HAOI2015]樹上染色

[2019.2.28]BZOJ4033 [HAOI2015]樹上染色

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]樹上染色