专栏文章

P7565 [JOISC 2021] ビーバーの会合 2 (Day3) - Solution

P7565题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mingky2i
此快照首次捕获于
2025/12/02 02:04
3 个月前
此快照最后确认于
2025/12/02 02:04
3 个月前
查看原文
刚开始想了一个假贪心还觉得对完了。
首先,可期待点类似于树的重心,不过这是虚树的重心。
给出一个重要观察:可期待点构成树上的一条链。
  • 两个可期待点之间的点均可期待。
否则,取出该不可期待点,该点必定存在一个子树,关键点数大于它的其余子树之和。而两个可期待点至少有一个不在该子树中,该可期待点往这个子树方向走贡献变小,矛盾。
  • 不存在度数 3\ge 3 的可期待点(这里的度数仅计算可期待点与可期待点之间)。
注意到我们取出该点(关键点意义下)最小的可期待点子树,走到该点贡献变小,矛盾。

既然可期待点构成树上的一条链,很显然可以考虑点分治。
具体到点分治上,设当前点分治的根为 xx。我们不妨枚举一端 uu
假设要求关键点数为 kk,我们钦定路径 uvu \to v。如果我们可以把 k2\frac{k}{2} 塞进 uu 子树里面,把 k2\frac{k}{2} 塞进 vv 子树里面,那么这就构造出了一种合法方案。否则,我们肯定没办法构造出来另一种方案,因为不管怎么样,我们都不能在三个方向上同时布满关键点,还让 uvu \to v 有贡献。
然后我们相当于 szuk2sz_u \ge \frac{k}{2} 就可能贡献到 kk 上,这个东西我们可以直接搞成后缀最大值,并且考虑与前面的某个满足相同条件的 vv 合并。
时间复杂度 Θ(nlogn)\Theta(n \log n)
CPP
int n; vec<int> g[maxn]; int vis[maxn], rt, sz[maxn], W, S, d[maxn], ans[maxn];
void getrt(int u, int f = 0) {
	sz[u] = 1; int x = 0;
	for (int v : g[u]) {
		if (v == f || vis[v]) continue;
		getrt(v, u), sz[u] += sz[v];
		if (sz[v] > sz[x]) x = v;
	}
	int k = max(sz[x], S - sz[u]);
	if (k < W) rt = u, W = k;
}
int s[maxn], t[maxn];
void dfs(int u, int f) {
	d[u] = d[f] + 1; 
	for (int v : g[u]) if (!vis[v] && v != f) dfs(v, u);
	s[sz[u]] = max(s[sz[u]], d[u]);
}
void calc(int u) {
	vis[u] = 1; d[u] = 0; getrt(u, 0);
	for (int v : g[u]) {
		if (vis[v]) continue;
		dfs(v, u);
		per(i, sz[v] - 1, 1) s[i] = max(s[i], s[i + 1]);
		rep(i, 1, sz[v]) ans[i * 2] = max(ans[i * 2], s[i] + t[i]);
		rep(i, 1, sz[v]) t[i] = max(t[i], s[i]), s[i] = 0;
	}
	rep(i, 1, sz[u]) s[i] = t[i] = 0;
	for (int v : g[u]) {
		if (vis[v]) continue;
		rt = 0, S = sz[v], W = 1e9;
		getrt(v, u); calc(rt);
	}
}
int main() {
	scanf("%d", &n); 
	if (n == 1) puts("1"), exit(0);
	for (int i = 1, u, v; i < n; i++) scanf("%d%d", &u, &v), g[u].pb(v), g[v].pb(u);
	rt = 0, S = n, W = 1e9; getrt(1, 0); calc(rt);
	rep(i, 1, n) printf("%d\n", (i & 1) ? 1 : ans[i] + 1);
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...