[bzoj4033]树上染色

xiaoxiao2021-02-27  231

题目描述

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

DP

首先贡献很恶心,但我们可以这样考虑,只考虑每条边的贡献。 设f[i,j]表示以i为根的子树中染黑了j个点的最大贡献,这里只考虑了子树中每条边以及i的父亲边的贡献。 那么转移很显然。 复杂度呢?n^3? 我们合并并不需要每次枚举m那么多,实际上枚举量等于子树大小。 那么我们改改枚举,复杂度就是n^2了! 因为可以注意到任意两点会在其lca处被计算,实际上枚举量只有n^2。

#include<cstdio> #include<algorithm> #define fo(i,a,b) for(i=a;i<=b;i++) #define fd(i,a,b) for(i=a;i>=b;i--) #define min(a,b) (a<b?a:b) using namespace std; typedef long long ll; const int maxn=2000+10; ll f[maxn][maxn],g[maxn]; int h[maxn],go[maxn*2],dis[maxn*2],nxt[maxn*2],size[maxn]; int i,j,k,l,t,n,m,tot; void add(int x,int y,int z){ go[++tot]=y; dis[tot]=z; nxt[tot]=h[x]; h[x]=tot; } void dfs(int x,int y,int z){ int i,j,k,t=h[x]; while (t){ if (go[t]!=y){ dfs(go[t],x,dis[t]); fo(i,0,m) g[i]=0; fo(i,0,min(size[x],m)) fo(j,0,min(size[go[t]],m-i)) g[i+j]=max(g[i+j],f[x][i]+f[go[t]][j]); fo(i,0,m) f[x][i]=g[i]; size[x]+=size[go[t]]; } t=nxt[t]; } fd(i,m-1,0) f[x][i+1]=max(f[x][i+1],f[x][i]); size[x]++; fo(i,0,m){ f[x][i]+=(ll)z*i*(m-i); f[x][i]+=(ll)z*(size[x]-i)*(n-m-size[x]+i); } } int main(){ scanf("%d%d",&n,&m); fo(i,1,n-1){ scanf("%d%d%d",&j,&k,&l); add(j,k,l);add(k,j,l); } dfs(1,0,0); printf("%lld\n",f[1][m]); }
转载请注明原文地址: https://www.6miu.com/read-9225.html

最新回复(0)