古典叙事 · 技术札记

序章

山河有卷
人间有声

写代码,也写长风、旧城与未熄的灯。
愿每一篇随笔,都有自己的山水与回声。
阅览随笔

树的重心

定义

如果在树中选择某个节点并删除,这棵树将分为若干棵子树,统计子树节点数并记录最大值。取遍树上所有节点,使此最大值取到最小的节点被称为整个树的重心。、

性质

树的重心如果不唯一,则至多有两个,且这两个重心相邻。
以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。
树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
当某节点 u 的最大子树节点 v 的 size 的两倍小于 u的size,那么v就是这个子树的重心

求法

首先先计算子树的大小,用dfs求出所有子树的大小,以当前节点为根分为向上向下两棵树,向下的节点数为size V 那么向上的节点数为n-size V;
然后考虑节点x和其每个儿子节点y,若x子树的重心在y子树内,则x子树的重心一定在y子树的重心的上方,所以我们从y子树的重心往上走,找到更优的就更新,否则,再往上跳显然答案会更加劣,所以结束即可。
因为这样更新每次结束都是这个子树的重心,下次循环又是这个子树的开始,所以一共经过每个点一次,所以复杂度可以优化为o(n);

void dfs(i64 now ) {
	siz [ now ] = 1;//树的节点数
	ans [ now ] = now;//重心位置初始化为自己
	for (auto it : t [ now ]) {
			dfs(it);
			siz [ now ] += siz [ it ];
			weight [ now ] = max(weight [ now ] , siz [ it ]);//找到最大的子树节点数
	}
	for (auto it : t [ now ]) {
		i64 p = ans [ it ];//从儿子的重心开始向上遍历
		while (p != now) {
			if (max(weight[p], siz[now] - siz[p]) <= siz[now] / 2)//性质当某节点 u 的最大子树节点 v 的 size 的两倍小于 u的size,那么v就是这个子树的重心
			{
				ans[now] = p;//更新结果
				break;
			} else
				p = fa[p];//向上找
		}
	}
 }

模板题

https://codeforces.com/problemset/problem/685/B

题意

输入一棵树,判断每一棵子树的重心是哪一个节点.

输入

第一行两个整数 n,m代表节点数量和查询次数
第二行n个整数,分别代表第(2~n)个节点的父亲是哪个节点
接下来m行每行一个整数ai,代表查询以ai为根的子树重心是哪一个节点

AC代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using i64 = long long;
i64 siz [ 300005 ],weight[300005],ans[300005];//siz树的大小,weirht最大子树,ans重心位置
i64 fa [ 300005 ];//节点i的父节点
vector <i64> t [ 300005 ];//存储所有子节点
i64 n , m;
void dfs(i64 now ) {
	siz [ now ] = 1;//树的大小
	ans [ now ] = now;//重心位置初始化为自己
	for (auto it : t [ now ]) {
			dfs(it);
			siz [ now ] += siz [ it ];
			weight [ now ] = max(weight [ now ] , siz [ it ]);//找到最大的子树节点数
	}
	for (auto it : t [ now ]) {
		i64 p = ans [ it ];//从儿子的重心开始向上遍历
		while (p != now) {
			if (max(weight[p], siz[now] - siz[p]) <= siz[now] / 2)//性质当某节点 u 的最大子树节点 v 的 size 的两倍小于 u的size,那么v就是这个子树的重心
			{
				ans[now] = p;//更新结果
				break;
			} else
				p = fa[p];//向上找
		}
	}
 }
int main() {
	cin >> n >> m;
	for (int i = 2; i <=n; i++) {
		i64 x;
		cin >> x;
		fa [ i ] = x;
		t [ x ].push_back(i);
	}
	dfs(1 );
	while (m--) {
		i64 x;
		cin >> x;
		cout << ans [ x ] << endl;
	}
	return 0;
}
posted @ 2025-08-08 11:21  Morphis‘  阅读(28)  评论(0)    收藏  举报

特别策划 · CINEMATIC NOTES

风沙与孤骑

风起塞外,
胜负在刀剑之前。

“把复杂拆成秩序,把未知写成答案。”
Morphis · 山河一卷 愿你从这里出发,仍能听见山风。 影像:farfarSébastien Goldberg / Unsplash