CF #591 E. Paint the Tree//树形dp

题目

题意

给一颗树,你有无限种颜色,每种颜色只能用两次,给每个顶点染 k k k个颜色,如果一条边上两点(每个点 k k k个颜色),如果有任一相同颜色,那么这条边上的权值可以加到 a n s ans ans里,求 a n s ans ans的最大值。

思路

考虑到如果一个非根结点,如果他和 k k k个儿子匹配了,那么就不能和父亲匹配。
那么定义 d p [ u ] [ 0 ] , d p [ u ] [ 1 ] dp[u][0],dp[u][1] dp[u][0],dp[u][1],表示是否已经和 k k k个儿子匹配了。
然后分类讨论。
如果儿子数不足 k k k:那么 d p [ u ] [ 1 ] = − 1 dp[u][1]=-1 dp[u][1]=1, d p [ u ] [ 0 ] dp[u][0] dp[u][0]就是和所有儿子连。
否则:假设 u u u的每一个儿子都不连 u u u有一个贡献 n o l i n k nolink nolink,计算每一个儿子 v v v u u u连起来多出来的贡献(可能为负),排序取 k k k k − 1 k-1 k1个最大值。

/*   Author : Rshs
 *   Data : 2019-10-08-13.24
 */
#include
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 1e6+5;
vector<PII>g[MXN];
LL dp[MXN][2]; //1满,0不满
int cs[MXN],n,k;
void dfs(int u,int pr){
    dp[u][0]=dp[u][1]=0;
    for(auto i:g[u]){
        if(i.FI==pr) continue;
        dfs(i.FI,u);
    }
    if(cs[u]>=k){
        LL nolink=0;
        for(auto i:g[u]){
            if(i.FI==pr) continue;
            int v=i.FI;
            nolink+=max(dp[v][0],dp[v][1]);
        }
        vector<LL>cbt;
        for(auto i:g[u]){
            if(i.FI==pr) continue;
            int v=i.FI,co=i.SE;
            if(dp[v][1]==-1) cbt.push_back(co);
            else {
                cbt.push_back(-max(dp[v][0],dp[v][1])+dp[v][0]+(LL)co);
            }
        }
        sort(cbt.begin(),cbt.end());reverse(cbt.begin(),cbt.end());
        dp[u][0]=dp[u][1]=nolink;
        for(int i=0;i<k;i++) dp[u][1]+=cbt[i];
        for(int i=0;i<=k-2;i++){
            if(cbt[i]<0)break;
            dp[u][0]+=cbt[i];
        }
    }
    else {
        dp[u][1]=-1;
        for(auto i:g[u]){
            if(i.FI==pr) continue;
            int v=i.FI,co=i.SE;
            LL mx=dp[v][0]+(LL)co;
            if(dp[v][1]!=-1) mx=max(mx,dp[v][1]);
            dp[u][0]+=mx;
        }
    }
}
void Main(int avg){
    cin>>n>>k;
    for(int i=1;i<=n;i++) g[i].clear();
    for(int i=1;i<n;i++){
        int sa,sb,sc;scanf("%d %d %d",&sa,&sb,&sc);
        g[sa].push_back(MP(sb,sc));
        g[sb].push_back(MP(sa,sc));
    }
    for(int i=1;i<=n;i++) cs[i]=SZ(g[i])-(i!=1);
    dfs(1,-1);
    cout<<max(dp[1][0],dp[1][1])<<'\n';
}
int main(){
    int cas;cin>>cas;for(int i=1;i<=cas;i++)Main(i);
    return 0;
}

你可能感兴趣的:(CF #591 E. Paint the Tree//树形dp)