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}\)了 难度提高+

posted @ 2020-01-19 11:59  Hock  阅读(421)  评论(0编辑  收藏  举报