Codeforces 1240C Paint the Tree 题解

这篇博客介绍了Codeforces 1240C问题的解决方案,涉及一棵最多50万节点的树,每个节点限制使用k种颜色,且颜色不能超过两次。内容围绕如何最大化边权值展开,通过状态dp[i][0/1]表示以i为根节点,是否选择与父节点相连的颜色进行讨论,并详细解析了状态设置的原因和转移方程,最后给出了代码实现。

题意简述

给定一个有nnn个节点的树,n<=5e5n<=5e5n<=5e5。每个点只能染精确的kkk种颜色,有无限种颜色珂供选择,但是每种颜色不能出现超过两次。如果一条边连接的两个点的两个颜色中有至少一个共同的,这条边就会产生它边权的权值。合理分配使权值最大,输出最大的权值。

思路框架

dp[i][0/1]dp[i][0/1]dp[i][0/1]表示以iii为根,是/否选择iiiiii的父亲那一条边,的最大权值和。转移即珂。

具体思路

分两个部分。

Part 1. 状态

那么我们为什么这么设状态呢。。。

套路

原因是这样的,我们一开始肯定能想到设前面的dp[i]dp[i]dp[i]表示以iii为根的答案。但是,如果只有这个,我们很难判断一个儿子是否能连上来,因为要连上来就需要有一个颜色与父亲对应,而和儿子只能用k−1k-1k1个颜色,这状态就不对,或者说,我们少一个状态。

所以我们考虑设dp[i][0/1]dp[i][0/1]dp[i][0/1],加上后面那一维。

Part 2. 转移

首先,把所有儿子的dp[son][1]dp[son][1]dp[son][1]加起来显然是一种选择。记这个默认值为SSS

然后我们发现,还珂能有儿子连到父亲的边的情况。这种情况,就是dp[son][0]+wdp[son][0]+wdp[son][0]+wwww是这条边的边权。如果这种情况的确更好,我们要把sonsonson这个儿子的默认值dp[son][1]dp[son][1]dp[son][1]换成dp[son][0]+wdp[son][0]+wdp[son][0]+w,对SSS的影响很显然就是S=S−dp[son][1]+dp[son][0]+wS=S-dp[son][1]+dp[son][0]+wS=Sdp[son][1]+dp[son][0]+w。但是我们能接到儿子的边最多就只有kkk条,因为我们现在考虑的uuu点只能染kkk种颜色。

那咋整嘛。也好办,我们把后半部分dp[son][0]+w−dp[son][1]dp[son][0]+w-dp[son][1]dp[son][0]+wdp[son][1]记下来,从大到小排个序。如果是dp[i][1]dp[i][1]dp[i][1],选前面kkk个即珂;但是dp[i][0]dp[i][0]dp[i][0]只能选前面k−1k-1k1个,因为还有留一个给父亲。当然,如果这个值是负的,说明选了之后不会更优。一个是负的,后面肯定都是负的了,于是直接 breakbreakbreak掉,不管了。

然后就结束了

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define N 1666666
    #define lovelive long long 
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)

    class Graph
    {
        public:
            int head[N];
            int EdgeCount;
            struct Edge
            {
                int To,Label,Next;
            }Ed[N<<1];
            void clear(int _len) 
            {
                memset(Ed,-1,sizeof(Edge)*(_len+10));
                memset(head,-1,sizeof(int)*(_len+10));
                EdgeCount=-1;
            }
            void AddEdge(int u,int v,int w=1)
            {
                Ed[++EdgeCount]=(Edge){v,w,head[u]};
                head[u]=EdgeCount;
            }
            void Add2(int u,int v,int w=1) {AddEdge(u,v,w);AddEdge(v,u,w);}
            int Start(int u) {return head[u];}
            int To(int u){return Ed[u].To;}
            int Label(int u){return Ed[u].Label;}
            int Next(int u){return Ed[u].Next;}
    }G;

    int n,k;
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(n),R1(k);
        G.clear(n);
        F(i,1,n-1)
        {
            int u,v,w;R1(u),R1(v),R1(w);
            G.Add2(u,v,w);
        } 
    }

    lovelive dp[N][2];
    //0: choose father
    //1: don't choose father
    void DFS(int u,int f)
    {
        lovelive sum=0;
        vector<lovelive> son;son.clear();
        Tra(i,u)
        {int v=__v;
            if (v!=f)
            {
                DFS(v,u);
                int d=dp[v][0]+G.Label(i)-dp[v][1];
                sum+=dp[v][1];
                son.push_back(d);
            }
        }
        dp[u][0]=dp[u][1]=sum;
        sort(son.begin(),son.end(),greater<lovelive>());
        F(i,0,min(k-1,(int)son.size()-1))
        {
            int v=son[i];
            if (v>0)
            {
                dp[u][1]+=v;
                if (i<k-1) dp[u][0]+=v;
            }
        }
    }
    void Soviet()
    {
        F(i,1,n) dp[i][0]=dp[i][1]=0;
        DFS(1,1);
        printf("%I64d\n",dp[1][1]);
    }

    #define Flan void
    Flan IsMyWife()
    {
        int t;R1(t);
        while(t--)
        {
            Input();
            Soviet();
        }
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值