BZOJ 4033 [HAOI2015]树上染色 (树形背包)

题意

2000个点的带边权的树,其中m个点染黑色,其他点染白色,问你怎么染可以让任意两个同色的点的距离之和最小

思路

经典的套路
先看每一条边的贡献,对于每条边(x,y),其中fa[y]=x
那么如果y子树的染色数确定,这条边的贡献也就确定了
令f[i][j]为i这棵子树染了j个黑点的答案,那么对于i的每个儿子,显然是个背包问题
套个树形背包的板子就行了

代码

vector<pair<int,ll> >v[maxn];
#define mp make_pair
int n,m;
ll ans;
ll f[2222][2222];
int sz[maxn];
ll g(int x, int i, int num){
    int y = v[x][i].fst;
    ll w = v[x][i].sc;
    return w*num*(m-num)+w*(sz[y]-num)*(n-sz[y]-(m-num));
}
void dfs(int x, int fa){
    sz[x]=1;
    for(int i = 0; i < (int)v[x].size(); i++){
        int y = v[x][i].fst;
        ll w = v[x][i].sc;
        if(y==fa)continue;
        dfs(y,x);
        for(int j = min(sz[x],m); j >= 0 ; j--){
            for(int k = min(sz[y],m); k >= 0; k--){
                ll tmp = f[x][j]+f[y][k]+g(x,i,k);
                f[x][j+k]=max(f[x][j+k],tmp);
            }
        }
        sz[x]+=sz[y];
    }

}
int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i < n; i++){
        int x,y;
        ll w;
        scanf("%d %d %lld", &x, &y, &w);
        v[x].pb(mp(y,w));
        v[y].pb(mp(x,w));
    }
    dfs(1,0);
    printf("%lld",f[1][m]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wrjlinkkkkkk/p/12806173.html