树形DP
神题一点不会。。。
考虑每条边对答案的贡献。设 f[x][i] f [ x ] [ i ] 表示以 x x 为根的子树中有个黑点时对答案的贡献,则有 f[x][i]=max{f[j][k]+w} f [ x ] [ i ] = m a x { f [ j ] [ k ] + w } ,其中 j j 为的儿子, w w 表示到 j j 这条边对答案的贡献,即子树内黑/白点的数量子树外黑/白点的数量 ∗ ∗ <script type="math/tex" id="MathJax-Element-22">*</script>这条边的长度。
注意循环的正反和范围,具体见代码。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; LL d; }ed[N<<1];
int n,m,k,h[N],fa[N],sz[N];
LL f[N][N];
#define addedge(x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k
void dfs(int x){
sz[x]=1,f[x][0]=f[x][1]=0;
for (int i=h[x],v;i;i=ed[i].nxt)
if ((v=ed[i].to)!=fa[x]){
fa[v]=x,dfs(v);
for (int j=min(sz[x]+sz[v],m);~j;j--)
//这里要倒序,或者另开一个数组存上一次的结果
for (int p=max(0,j-sz[x]);p<=min(sz[v],j);p++){
//这里开始范围要取max,因为黑点个数不可能超过子树大小
LL w=(1ll*p*(m-p)+1ll*(sz[v]-p)*(n-m-sz[v]+p))*ed[i].d;
f[x][j]=max(f[x][j],f[x][j-p]+f[v][p]+w);
}
sz[x]+=sz[v];
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1,x,y,z;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z),addedge(y,x,z);
}
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
f[i][j]=-1e9;//一定要给负
return dfs(1),printf("%lld",f[1][m]),0;
}