Codeforces 1240C Paint the Tree 题解

题意简述

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

思路框架

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

具体思路

分两个部分。

Part 1. 状态

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

套路

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

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

Part 2. 转移

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

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

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

然后就结束了

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#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;
}
w