[BZOJ]4033: [HAOI2015]树上染色 树形DP

来源:互联网 发布:淘宝做代理靠谱吗 编辑:程序博客网 时间:2024/05/15 00:49

Description

有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。

题解:

显然树形DP。状态很好设计,f[i][j]表示以i为根的子树有j个黑点的最大收益,然后枚举每个子树有多少个黑点,转移的时候考虑当前这条边的贡献,就可以了。然而这种转移的复杂度乍看上去是O(n3)的,实际上复杂度却是O(n2)的,ydc的uoj博客上有证明,我就再写写:考虑这三重循环,实际上可以看作是枚举点对(u,v),而(u,v)只会在dfs(LCA(u,v))的时候被枚举到,所以实际上每对点只被枚举了一次,有n2对点,所以时间复杂度为O(n2)

代码:

#include<bits/stdc++.h>using namespace std;#define LL long longconst int Maxn=2010;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}    return x*f;}int n,K,size[Maxn];struct Edge{int y,next;LL d;}e[Maxn<<1];int last[Maxn],len=0;void ins(int x,int y,LL d){int t=++len;e[t].y=y;e[t].d=d;e[t].next=last[x];last[x]=t;}LL f[Maxn][Maxn],temp[Maxn];void dfs(int x,int fa){    size[x]=1;    for(int i=last[x];i;i=e[i].next)    {        int y=e[i].y;        if(y==fa)continue;        dfs(y,x);        memset(temp,0,sizeof(temp));        for(int j=0;j<=size[x];j++)        for(int k=0;k<=size[y];k++)        if(j+k<=K)temp[j+k]=max(temp[j+k],f[x][j]+f[y][k]+e[i].d*((LL)(k*(K-k))+(LL)((size[y]-k)*(n-K-size[y]+k))));        for(int j=0;j<=K;j++)f[x][j]=temp[j];        size[x]+=size[y];    }}int main(){    n=read();K=read();    for(int i=1;i<n;i++)    {        int x=read(),y=read();LL d=(LL)(read());        ins(x,y,d);ins(y,x,d);    }    dfs(1,0);printf("%lld",f[1][K]);}
原创粉丝点击