【BZOJ4033】【HAOI2015】树上染色

mac2022-07-01  5

Description

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

Input

第一行包含两个整数 N, K 。接下来 N-1 行每行三个正整数 fr, to, dis , 表示该树中存在一条长度为 dis 的边 (fr, to) 。输入保证所有点之间是联通的。

Output

输出一个正整数,表示收益的最大值。

Sample Input

3 1 1 2 1 1 3 2

Sample Output

3

Hint

对于 100% 的数据,\(0 \leq K \leq N \leq 2000\)

Solution

考虑树形DP,状态应为第i个点的子树内有j个黑色点对整棵树的贡献,问题就转换为了树形背包,对于一条边的贡献=两边的黑色点数之积+两边的白色点之积,利用这一点进行树形DP即可,每次枚举子树内黑色点个数,与当前子树内黑色点个数,进行统计答案即可,将上界改为子树大小,可以将时间复杂度优化至\(O(n^2)\).

Code

#include <stdio.h> #include <string.h> #define MN 2005 #define R register #define ll long long #define file(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout); #define end fclose(stdin);fclose(stdout) inline int read(){ R int x; R bool f; R char c; for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-'); for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0'); return f?-x:x; } int n,k,to[MN<<1],nt[MN<<1],v[MN<<1],h[MN],sz[MN],en;ll f[MN][MN]; inline void ins(int x,int y,int vl){to[++en]=y,nt[en]=h[x],h[x]=en,v[en]=vl;} inline int min(int a,int b){return a<b?a:b;} inline ll max(ll a,ll b){return a>b?a:b;} inline void dp(int u,int fa){ f[u][0]=f[u][1]=0;sz[u]=1; for (R int i=h[u]; i; i=nt[i]) if (to[i]!=fa) dp(to[i],u),sz[u]+=sz[to[i]]; for (R int i=h[u]; i; i=nt[i]) if (to[i]!=fa) for (R int j=min(k,sz[u]); j>=0; --j) for (R int l=0; l<=min(j,sz[to[i]]); ++l) if (~f[u][j-l]){ ll val=(1ll*l*(k-l)+(1ll*sz[to[i]]-l)*(1ll*n-k+l-sz[to[i]]))*v[i]; f[u][j]=max(f[u][j],f[u][j-l]+f[to[i]][l]+val); } } int main(){ memset(f,-1,sizeof(f)); n=read(),k=read(); for (R int i=1; i<n; ++i){ R int x=read(),y=read(),v=read(); ins(x,y,v); ins(y,x,v); }dp(1,0);printf("%lld\n",f[1][k]); return 0; }

转载于:https://www.cnblogs.com/Melacau/p/BZOJ4033.html

最新回复(0)