1012 [HAOI2015]树上染色 树上背包+计算边的贡献
链接:https://ac.nowcoder.com/acm/contest/25022/1012
来源:牛客网
题目描述
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。
将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少。
输入描述:
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。N ≤ 2000,0 ≤ K ≤ N
输出描述:
输出一个正整数,表示收益的最大值。
分析
将黑节点和白节点间的距离转化成每条边的对这个距离所作出的贡献
比如一条边权值是5,左边有4个黑节点,右边有6个黑节点,这条边的贡献就是 4 * 6 * 5 。因为有4 * 6 条链是经过这条边的。
设siz[u] 为u为根的子树的大小
f[u][j] 表示 u 为根选择 j 个黑色节点的最大权值。
就是经典的选择问题,选择在哪里放多少黑色节点,所以用背包求解。
状态转移:f[u][j] = max(f[u][j],f[son][k] + f[u][j - k] + 1ll * w[i] * ( (m - k) * (k) + (n - m - (siz[son] - k) ) * ( siz[son] - k) ) ; /
总贡献 = 【左边的黑色节点数 * 右边的黑色节点数 + 左边的白色节点数 * 右边的白色节点数 】 * 权值
//-------------------------代码---------------------------- // #define int ll const int N = 2e6+10; int n,m; int h[N],ne[N],w[N],e[N],idx,siz[N]; ll f[3000][2050]; void add(int a,int b,int c) { e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx ++ ; } void dfs(int u,int fa) { f[u][0] = f[u][1] = 0; siz[u] = 1; for(int i = h[u];~i;i=ne[i]) { int son = e[i]; if(son == fa) continue; dfs(son,u); siz[u] += siz[son]; of(j,min(m,siz[u]),0) { fo(k,0,min(j,siz[son])) { f[u][j] = max(f[u][j],f[u][j - k] + f[son][k] + 1ll * w[i] * ( k * (m - k) + (siz[son] - k) * (n - m - (siz[son] - k)) ) ); } } } } void solve() { cin>>n>>m; ms(h,-1);memset(f, -0x3f, sizeof f); fo(i,1,n-1) { int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,c); } dfs(1,-1); cout<<f[1][m]<<endl; } signed main(){ AC(); clapping();TLE; // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------