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;
}
posted @ 2026-10-19 15:22  ptlks  阅读(15)  评论(0)    收藏  举报