#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 21 2 3
1 5 1
2 3 1
2 4 2
Sample Output
17【样例解释】
将点1,2染黑就能获得最大收益。
Hint
2017.9.12新加数据一组 By GXZlegend
Source
鸣谢bhiaibogf提供SolutionSolution
#include<ctime> #include<cstdio> #include<cstdlib> #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) const int N=2000+5; const int M=N<<1; typedef long long ll; int tot,to[M],val[M],next[M],head[N],size[N];bool vis[N]; int n,K;ll f[N][N]; inline void add(int x,int y,int z){ to[++tot]=y;val[tot]=z;next[tot]=head[x];head[x]=tot; } void dp(int x){ int y,w,p,q; size[x]=1;vis[x]=1; for(int i=head[x];i;i=next[i]){ if(vis[y=to[i]]) continue; dp(y); w=val[i]; p=min(size[x],K); q=min(size[y],K);//常数优化 for(int j=p;~j;j--){ for(int k=q;~k;k--){ ll tv=1LL*(k*(K-k)+(n-size[y]-K+k)*(size[y]-k))*w; f[x][j+k]=max(f[x][j+k],f[x][j]+f[y][k]+tv); } } size[x]+=size[y];//常数优化 } } int main(){ srand(time(0));srand(rand()); scanf("%d%d",&n,&K);K=min(K,n-K); for(int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z); int root=rand()%n+1;//常数优化 dp(root); printf("%lld",f[root][K]); return 0; }
参考:
https://acxblog.site/archives/sol-bzoj-4033.html