2026百度之星决赛 第9题
题意:
给定一棵 \(n\) 个点的树,每个点可以染成红、绿、白三种颜色之一。若一条由三个点构成的链,其颜色按顺序为红、绿、白,则称为好链。求通过染色能得到的好链数量最大值。
思路:
一条好链一定以中间点为绿色,并且左右两个端点分别染红、白。因此若绿色点 \(x\) 有 \(a\) 个红邻居、\(b\) 个白邻居,则以 \(x\) 为中心的好链数为 \(a\cdot b\)。
不难发现红白两色是等价的,所以对每个点 \(x\) 设计三个状态:
- \(dp[0][x]\):\(x\) 为绿色,且父节点为绿色时的最大值;
- \(dp[1][x]\):\(x\) 为红色或白色时的最大值;
- \(dp[2][x]\):\(x\) 为绿色,且父节点为红色或白色时的最大值。
若 \(x\) 不是绿色,则儿子 \(u\) 要么不是绿色,贡献 \(dp[1][u]\);要么是绿色且父节点 \(x\) 为红/白,贡献 \(dp[2][u]\),所以
\[dp[1][x]=\sum_{u\in son(x)}\max(dp[1][u],dp[2][u]).
\]
若 \(x\) 为绿色,先令所有儿子都是绿色,初值为
\[S=\sum_{u\in son(x)}dp[0][u].
\]
把儿子 \(u\) 改成红/白会获得收益 \(dp[1][u]-dp[0][u]\),同时它还能作为以 \(x\) 为中心的链的端点。若一共选择 \(t\) 个儿子染成红/白,则把它们尽量平均分成红、白两色,以 \(x\) 为中心的贡献最大为
\[\left\lfloor\frac t2\right\rfloor\left\lceil\frac t2\right\rceil.
\]
固定 \(t\) 时,应选择 \(dp[0][u]-dp[1][u]\) 最小的 \(t\) 个儿子,因此把邻居按 \(dp[0][u]-dp[1][u]\) 升序排序,枚举前缀即可。
于是
\[dp[0][x]=\max_{t\ge 0}\left(S+\sum_{i=1}^{t}(dp[1][u_i]-dp[0][u_i])+
\left\lfloor\frac t2\right\rfloor\left\lceil\frac t2\right\rceil\right).
\]
对于 \(dp[2][x]\),父节点也可作为一个红/白端点,共有 \(t+1\) 个红/白邻居,最大贡献比原来多
\[\left\lceil\frac t2\right\rceil=t-\left\lfloor\frac t2\right\rfloor.
\]
所以
\[dp[2][x]=\max_{t\ge 0}\left(S+\sum_{i=1}^{t}(dp[1][u_i]-dp[0][u_i])+
\left\lfloor\frac t2\right\rfloor\left\lceil\frac t2\right\rceil+
\left(t-\left\lfloor\frac t2\right\rfloor\right)\right).
\]
根节点没有父节点,答案为
\[\max(dp[0][1],dp[1][1]).
\]
每个点的儿子需要排序,总复杂度为 \(O(n\log n)\)。
代码
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define endl '\n'
using namespace std;
const int N = 5e5+10, M = 19;
vector<int>g[N];
int dp[3][N];
bool cmp(int x,int y){
return dp[0][x]-dp[1][x]<dp[0][y]-dp[1][y];
}
void dfs(int x, int fa) {
int s=0,t=0;
dp[0][x]=dp[1][x]=dp[2][x]=0;
for (auto u : g[x]) {
if (u != fa) {
dfs(u, x);
s+=dp[0][u];
dp[1][x]+=max(dp[2][u],dp[1][u]);
}
}
sort(g[x].begin(),g[x].end(),cmp);
dp[0][x]=s;
dp[2][x]=s;
for (auto u : g[x]) {
if (u != fa) {
s-=dp[0][u];
s+=dp[1][u];
t++;
dp[0][x]=max(dp[0][x],s+(t/2)*(t-t/2));
dp[2][x]=max(dp[2][x],s+(t/2)*(t-t/2)+(t-t/2));
}
}
}
void solve() {
int n, m;
cin >> n;
for (int i = 1; i <= n; i++) {
g[i].clear();
dp[0][i]=dp[1][i]=dp[2][i]=0;
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,0);
// for (int i = 1; i <= n; i++) {
// cout<<i<<':';
// for(auto u:g[i]){
// cout<<u<<' ';
// }cout<<endl;
// cout<<dp[0][i].first<<' '<<dp[0][i].second<<' '<<dp[1][i].first<<' ';
// cout<<dp[2][i].first<<' '<<dp[2][i].second<<endl;
// }
int ans=max(dp[0][1],dp[1][1]);
cout<<ans<<endl;
}
signed main( ) {
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号