专栏文章
P7565 [JOISC 2021] ビーバーの会合 2 (Day3) - Solution
P7565题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mingky2i
- 此快照首次捕获于
- 2025/12/02 02:04 3 个月前
- 此快照最后确认于
- 2025/12/02 02:04 3 个月前
刚开始想了一个假贪心还觉得对完了。
首先,可期待点类似于树的重心,不过这是虚树的重心。
给出一个重要观察:可期待点构成树上的一条链。
- 两个可期待点之间的点均可期待。
否则,取出该不可期待点,该点必定存在一个子树,关键点数大于它的其余子树之和。而两个可期待点至少有一个不在该子树中,该可期待点往这个子树方向走贡献变小,矛盾。
- 不存在度数 的可期待点(这里的度数仅计算可期待点与可期待点之间)。
注意到我们取出该点(关键点意义下)最小的可期待点子树,走到该点贡献变小,矛盾。
既然可期待点构成树上的一条链,很显然可以考虑点分治。
具体到点分治上,设当前点分治的根为 。我们不妨枚举一端 。
假设要求关键点数为 ,我们钦定路径 。如果我们可以把 塞进 子树里面,把 塞进 子树里面,那么这就构造出了一种合法方案。否则,我们肯定没办法构造出来另一种方案,因为不管怎么样,我们都不能在三个方向上同时布满关键点,还让 有贡献。
然后我们相当于 就可能贡献到 上,这个东西我们可以直接搞成后缀最大值,并且考虑与前面的某个满足相同条件的 合并。
时间复杂度 。
CPPint 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 条评论,欢迎与作者交流。
正在加载评论...