bzoj 4033: [HAOI2015]树上染色
Description
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并
将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。
Input
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。
N<=2000,0<=K<=N
Output
输出一个正整数,表示收益的最大值。
Sample Input
5 2
1 2 3
1 5 1
2 3 1
2 4 2
1 2 3
1 5 1
2 3 1
2 4 2
Sample Output
17
【样例解释】
将点1,2染黑就能获得最大收益。
【样例解释】
将点1,2染黑就能获得最大收益。
题解:
小小套路题,我们要想办法简化状态,我们如果简单dp,需要知道选的白点和黑点分别是什么.
但是我们可以改变成求每一条边对答案贡献多少
同样地,f[i][j]表示i的子树中,选了j个黑点,对答案的最大贡献,这样就没了后效性.
已知i子树中选了j个黑点,那么其他的黑点必然在i的上方,且必然经过i到fa[i]上边,所以这条边贡献直接乘以j*(m-j)即可,白点同理
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #define RG register 8 #define il inline 9 using namespace std; 10 typedef long long ll; 11 const int N=4005; 12 int n,m,num=0,nxt[N<<1],to[N<<1],dis[N<<1],head[N];ll f[N][N]; 13 void addedge(int x,int y,int z){ 14 nxt[++num]=head[x];to[num]=y; 15 dis[num]=z;head[x]=num; 16 } 17 int sz[N]; 18 void dfs(int x,int last){ 19 RG int u;ll val,tmp; 20 sz[x]=1; 21 for(RG int i=head[x];i;i=nxt[i]){ 22 u=to[i]; 23 if(u==last)continue; 24 dfs(u,x); 25 for(int j=min(sz[x],m);j>=0;j--){ 26 for(int k=min(m-j,sz[u]);k>=0;k--){ 27 val=(ll)k*(m-k)*dis[i]+(ll)(sz[u]-k)*(n-m-(sz[u]-k))*dis[i]; 28 tmp=f[x][j]+f[u][k]+val; 29 if(tmp>f[x][k+j])f[x][j+k]=tmp; 30 } 31 } 32 sz[x]+=sz[u]; 33 } 34 } 35 void work() 36 { 37 int x,y,z; 38 scanf("%d%d",&n,&m); 39 for(int i=1;i<n;i++){ 40 scanf("%d%d%d",&x,&y,&z); 41 addedge(x,y,z);addedge(y,x,z); 42 } 43 dfs(1,1); 44 printf("%lld\n",f[1][m]); 45 } 46 int main() 47 { 48 work(); 49 return 0; 50 }