CSP-S 2019 树的重心
\(\text{树的重心}\)
小简单的课后作业是求出\(S\)单独删去每条边后,分裂出的两个子树的重心编号和之和
请你帮小简单完成作业
subtask1 \(\text {期望得分 40pts}\)
\(O(n)\)枚举删去哪一条边 然后\(dfs\)找儿子 简单粗暴 时间复杂度\(O(n^2)\)
subtask2 \(\text{期望得分 55pts}\)
\(O(n)\)扫过去 把重心弄出来就行了
std \(\text{期望得分 100pts}\)
有一个性质就是重心一定在这个树重链上 而且 每一个点的重儿子只有一个 很容易想到用倍增去写 然后我们可以\(dfs\)一遍 边\(dfs\)边换根 (\(\text{size}\)和\(\text{son}\))的变化 时间复杂度\(O(n \log n)\)
inline void work (int u) {
for (int i = 1; i <= 20; i ++ ) f[u][i] = f[f[u][i - 1]][i - 1];
}
inline void dfs (int u, int fa) {
size[u] = 1; Fa[u] = fa;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == fa) continue;
dfs (v, u);
size[u] += size[v];
if (size[v] > size[son[u]]) son[u] = v;
}
f[u][0] = son[u];
work (u);
}
inline void solve (int u, int sz) {
for (int i = 20; i >= 0; i -- ) {
if (f[u][i] && size[f[u][i]] * 2 >= sz) u = f[u][i];
}
ans += u;
if (size[u] * 2 == sz) ans += Fa[u];
}
inline void dp (int u, int fa) {
int s1 = 0, s2 = 0;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (size[v] > size[s1]) s2 = s1, s1 = v;
else if (size[v] > size[s2]) s2 = v;
}
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == fa) continue;
int x = s1;
if (v == x) x = s2;
size[u] -= size[v];
f[u][0] = x;
work (u);
solve (v, size[v]); solve (u, size[u]);
size[v] += size[u]; Fa[u] = v;
dp (v, u);
size[v] -= size[u]; size[u] += size[v];
}
f[u][0] = son[u];
work (u);
Fa[u] = fa;
}
分析
\(55\) 考场暴力标配 难度普及+
\(100\) 用倍增不难想主要是分析性质 想到重儿子就\(\text{ok}\)了 难度提高+