CF1223E Paint the Tree(树形DP)
给出了一个由n个顶点组成的加权树。回想一下,树是一个没有圈的连通图。顶点ui和vi通过带权重wi的边连接。
让我们将树的k-着色定义为每个顶点的k-着色,这样每个颜色的使用次数就不会超过两次。你可以假设你有无限多的颜色可用。我们说,如果一条边的端点共享至少一种颜色(即存在一种指定给两个端点的颜色),那么它在给定的k-着色中是饱和的。
我们还将k-着色的值定义为饱和边的权重之和。
请计算给定树的k-着色的最大可能值。
你必须回答与q无关的问题。
第一行包含一个整数q(1≤q≤5⋅105)-查询数。
每个查询的第一行包含两个整数n和k(1≤n,k≤5⋅105)-树中的顶点数和分配给每个顶点的颜色数。
/* * cf1223E * 题意: * 给出一棵树,选择若干条边使边权值和最大,要求每个节点至多被k条边覆盖 * 题解: * 树形dp。 * 考虑子节点和父节点的关系可知有两种情况: * (1)子节点已经连完k条边则不可以与父节点连边。 * (2)子节点留一条边和父节点连边。 * 可以用dp[0][u]表示选择k条当前节点与子节点的边得到的以u为节点的子树的最大值 * dp[1][u]表示选择k-1条当前节点与子节点的边得到的以u为节点的子树的最大值,与子树相连的边可以用优先队列维护 */ #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=5e5+2; struct node { int u,v,next; ll w; }edge[maxn<<1]; int head[maxn],tot; int N,K; ll dp[2][maxn],ans; void addedge (int u,int v,ll w) { edge[tot].u=u; edge[tot].v=v; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot++; } inline void dfs (int u,int pre) { priority_queue<ll> q; ll x=0; for (int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if (v==pre) continue; dfs(v,u); x+=dp[0][v]; if (dp[1][v]+edge[i].w-dp[0][v]>0) q.push({dp[1][v]+edge[i].w-dp[0][v]}); } ll w=1; while (!q.empty()&&w<K) { ll t=q.top(); q.pop(); x+=t; w++; } dp[0][u]=dp[1][u]=x; if (!q.empty()) dp[0][u]+=q.top(); ans=max(ans,max(dp[1][u],dp[0][u])); } int main () { int Q; scanf("%d",&Q); while (Q--) { ans=0; tot=0; scanf("%d%d",&N,&K); for (int i=0;i<=N;i++) { dp[1][i]=0; dp[0][i]=0; head[i]=-1; } for (int i=1;i<N;i++) { int u,v;ll w; scanf("%d%d%lld",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } dfs(1,0); printf("%lld\n",ans); } }
接下来的n-1行中的每一行描述树的一个边。边i由三个整数ui、vi和wi(1≤ui,vi≤n,ui≠vi,1≤wi≤105)表示-它连接的顶点的标签和边的权重。可以保证给定的边形成一棵树。
保证所有查询的n和不超过5⋅105。
对于每个查询,打印一个整数-给定树的k-着色的最大值。