[BZOJ 4033] 树上染色

Link:

BZOJ 4033 传送门

Solution:

此题用到了计算贡献的方法,

将 多条路径的路径和  $->$ $\sum_{i=1}^{n-1} w[i]*cnt[i]$

这样我们由找出所有路径再计算转化成了对每条边计算其的贡献

 

由于所有节点只用2种选择,接下来就是比较套路的树形DP了

设 $dp[i][j]$ 为在以 $i$ 为根的子树中,有$j$个黑点时的$MAX$。

这样按照$dfs$序依次处理每个节点$x$,对子树背包$DP$,最后再加上$w_{<x,father[x]>}$的贡献即可

Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int,int> P;

const int MAXN=2005*2;
vector<P> G[MAXN];
ll n,k,dp[MAXN][MAXN],sz[MAXN];

void tree_dp(int x,int anc,ll val)
{
    sz[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i].first;
        if(v==anc) continue;
        tree_dp(v,x,G[x][i].second);
        for(int p=min(sz[x],k);p>=0;p--)
            for(int q=min(sz[v],k-p);q>=0;q--)
                dp[x][p+q]=max(dp[x][p+q],dp[x][p]+dp[v][q]);
        sz[x]+=sz[v];
    }
    for(int i=0;i<=min(sz[x],k);i++) //统计贡献
        dp[x][i]+=val*((ll)i*(k-i)+(sz[x]-i)*((n-sz[x])-(k-i)));
}

int main()
{
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<n;i++)
    {
        ll x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
        G[x].push_back(P(y,z));
        G[y].push_back(P(x,z));
    }
    tree_dp(1,0,0);
    printf("%lld",dp[1][k]);
    return 0;
}

Review:

 1、计算贡献的思想:

多个整体 拆分成 每个个体$*$出现次数的和

只要能快速地计算出贡献,依次考虑每个个体即可

 

2、树形$DP$的套路和注意事项:

套路:树形$DP$大部分时候都是依据$dfs$序在对子树背包$DP$

Note:(1)背包$DP$要从后往前更新,防止多次计算

           (2)一般$size[x]$的更新都要放在对该子树更新完之后

 

3、点集的划分

该题的特殊性在于对点集严格划分为2堆,才能直接算出每种点的个数

当出现对点集严格划分的题目时,只要保存一个量,并考虑能否计算贡献

 

posted @ 2018-06-05 10:54  NewErA  阅读(142)  评论(0编辑  收藏  举报