[2019.2.28]BZOJ4033 [HAOI2015]树上染色

首先我们设\(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;
}

猜你喜欢

转载自www.cnblogs.com/xryjr233/p/BZOJ4033.html