[HAOI2015]树上染色
题目描述
有一棵点数为 N 的树,树边有边权。给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 。 将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。
输入输出格式
输入格式:
第一行包含两个整数 N, K 。接下来 N-1 行每行三个正整数 fr, to, dis , 表示该树中存在一条长度为 dis 的边 (fr, to) 。输入保证所有点之间是联通的。
输出格式:
输出一个正整数,表示收益的最大值。
输入输出样例
说明
对于 100% 的数据, 0<=K<=N <=2000
设$f[i][j]$表示i的子树选了j个黑点的边贡献和
因为点的贡献不好维护
如果一条边,它下面有j个黑点,那么这条边的贡献:
1.w*j*(k-j) 黑点贡献
2.w*(size[v]-j)*(n-size[v]-k+j) 白点贡献
然后树形背包
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 typedef long long lol; 8 struct Node 9 { 10 int next,to; 11 int dis; 12 }edge[4001]; 13 lol ans,f[2001][2001]; 14 int size[2001],k; 15 int num,head[2001],n; 16 void add(int u,int v,int d) 17 { 18 num++; 19 edge[num].next=head[u]; 20 head[u]=num; 21 edge[num].to=v; 22 edge[num].dis=d; 23 } 24 void dfs(int x,int pa) 25 {int i; 26 int j,l; 27 size[x]=1; 28 f[x][0]=f[x][1]=0; 29 for (i=head[x];i;i=edge[i].next) 30 { 31 int v=edge[i].to; 32 if (v==pa) continue; 33 dfs(v,x); 34 size[x]+=size[v]; 35 for (j=min(k,size[x]);j>=0;j--) 36 { 37 for (l=0;l<=min(j,size[v]);l++) 38 if (f[x][j-l]!=-1&&f[v][l]!=-1) 39 { 40 lol val=1ll*l*(k-l)*edge[i].dis+1ll*(size[v]-l)*(n-k-size[v]+l)*edge[i].dis; 41 f[x][j]=max(f[x][j],f[x][j-l]+f[v][l]+val); 42 } 43 } 44 } 45 } 46 int main() 47 {int i,u,v; 48 int d; 49 cin>>n>>k; 50 for (i=1;i<=n-1;i++) 51 { 52 scanf("%d%d%d",&u,&v,&d); 53 add(u,v,d);add(v,u,d); 54 } 55 memset(f,-1,sizeof(f)); 56 dfs(1,0); 57 cout<<f[1][k]; 58 }