HAOI2015 树上染色

一棵带边权的树,你需要把 $k$ 个点染成黑色,剩下的染成白色,你会获得黑点间两两间距离之和 + 白点间两两距离之和的收益

求收益最大值

$n \leq 5000$

sol:

树形 dp

显然一条边的贡献是 $边权 \times (左边白色 \times 右边白色 + 左边黑色 \times 右边黑色)$

$f_{(x,i)}$ 表示 $x$ 子树内选了 $i$ 个黑点,子树里的边对全局答案的贡献

然后我们按树上背包转移,转移的时候加的贡献其实是一条父子边的贡献

我们假设在 $v$ 点下面放了 $w$ 个黑点

这条边的贡献就是 $$边权 \times (w \times (k - w) + (size_v - k) \times (n - size_v - k + w))$$

 

还是很简单的

 

#include<bits/stdc++.h>
#define LL long long
#define int long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int inf = 1e16;
int n,m,nk;
struct EDG{int to,val;};
vector<EDG> G[2100];
int f[2100][2100],size[2100],tmp[2100];
inline void dfs(int x,int fa)
{
    size[x] = 1;
    for(int i=0;i<G[x].size();i++)
    {
        int to = G[x][i].to,len = G[x][i].val;
        if(to == fa)continue;
        dfs(to,x);
        fill(tmp,tmp+size[x]+size[to]+1,-inf);
        for (int j=0;j<=size[x];j++)
            for (int k=0;k<=size[to];k++)
                tmp[j+k]=max(tmp[j+k],f[x][j]+f[to][k]+(LL)len*((LL)k*(m-k)+(LL)(n-size[to]-m+k)*(size[to]-k)));
        size[x]+=size[to];
        for (int j=0;j<=size[x];j++) f[x][j]=tmp[j];
    }
}
signed main()
{
    n = read();m = read();
    nk = n - m;
    for(int i=2;i<=n;i++)
    {
        int u = read(),v = read(),w = read();
        G[u].push_back((EDG){v,w});
        G[v].push_back((EDG){u,w});
    }
    dfs(1,1);
    cout<<f[1][m];
}
View Code

 

转载于:https://www.cnblogs.com/Kong-Ruo/p/9891088.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值