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
#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 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)
    {
        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<1][m];
}
View Code

 

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

你可能感兴趣的:(HAOI2015 树上染色)