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

Sample Output

17
【样例解释】
将点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 }

 

posted @ 2017-08-09 22:16  PIPIBoss  阅读(240)  评论(0编辑  收藏  举报