cf1223E. Paint the Tree [树形dp]

传送门
题意:给出一棵树,选择若干条边使边权值和最大,要求每个结点至多被k条边覆盖
题解:(感觉做E题不那么吃力了)
树形dp。考虑子结点与父节点的关系可知有两种情况,一种情况子结点已经连完k条边则不可以与父节点连边,另一种是留一条边和父节点相连,所以可以很容易想到用dp[0][u]表示选择k条当前结点和子节点的边得到的以u为结点的子树最大值,dp[1][u]表示选择k-1条当前结点和子节点的边得到的以u为结点的子树最大值,与子树相连的边可以用优先队列维护

#include

using namespace std;
#define debug(x) cout<<#x<<" is "<
typedef long long ll;

const int maxn=5e5+5;

struct edge{
    int fr;
    int to;
    ll val;
    int nex;
}e[maxn<<1];

int head[maxn],cnt,n,k;
ll dp[2][maxn],ans;

void adde(int x,int y,ll z){
    e[cnt].fr=x;
    e[cnt].to=y;
    e[cnt].val=z;
    e[cnt].nex=head[x];
    head[x]=cnt++;
}

void dfs(int u,int f){
    priority_queue<ll>pq;
    ll x=0;
    for(int i=head[u];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(v==f)continue;
        dfs(v,u);
        x+=dp[0][v];
        if(dp[1][v]+e[i].val-dp[0][v]>0)pq.push({dp[1][v]+e[i].val-dp[0][v]});
    }
    //ll x=0;
    int w=1;
    while(!pq.empty()&&w<k){
        ll xx=pq.top();
        pq.pop();
        x+=xx;
        w++;
    }
    dp[0][u]=dp[1][u]=x;
    if(!pq.empty())dp[0][u]+=pq.top();
    ans=max(ans,max(dp[1][u],dp[0][u]));
  /*  debug(u);
    debug(dp[0][u]);
    debug(dp[1][u]);*/
}

int main(){
    int q;
    scanf("%d",&q);
    while(q--){
       // int n,k;
        cnt=0;
        ans=0;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            head[i]=-1;
            dp[0][i]=dp[1][i]=0;
        }
        for(int i=1;i<n;i++){
            int a,b;
            ll c;
            scanf("%d%d%lld",&a,&b,&c);
            adde(a,b,c);
            adde(b,a,c);
        }
        dfs(1,0);
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(树形dp)