树基础知识
植树节已过,小小的来点树
树的重心
树的重心是什么?
对于一棵无根树,设其中的一个节点作为根,则可以形成一棵有根树。
该树以根为分界,分为若干个子树,设其中最大子树具有的节点数为 x 。
所有节点里, x 值最小的节点就是该树的重心,也叫质心。
与其相似的解释:
如果在树中选择某个节点并删除,这棵树将分为若干棵子树,统计子树节点数并记录最大值。取遍树上所有节点,使此最大值取到最小的节点被称为整个树的重心。
(这里以及下文中的「子树」若无特殊说明都是指无根树的子树,即包括「向上」的那棵子树,并且不包括整棵树自身。)
重心的性质
重心相当于树的平衡点,其尽量均匀地将树分割成了若干部分。
在树分治以及树同构中,重心都具有相当重要的地位。
重心具有几个比较重要的性质:
①一棵树最少有一个重心,最多有两个重心,若有两个重心,则他们相邻(即连有直接边)。
②树上所有点到某个点的距离和里,到重心的距离和最小;若有两个重心,则其距离和相同。
③若以重心为根,则所有子树的大小都不超过整棵树的一半。否则可以通过平移使得最大子树的大小缩小至整树的一半,剩下子树的大小最大为 n/2−1 。此时新平移到的点才是真正的重心。
④在一棵树上添加或删除一个叶子节点,其重心最多平移一条边的距离。
⑤两棵树通过连一条边组合成新树,则新树重心在原来两棵树的重心的连线上。
如何求解重心?
按其定义来求即可。
我们任选一个节点作为根,然后跑DFS,在过程中记录每个节点各个子树的大小。对于每个节点,其还存在一个上方的子树(即从假设根到当前节点),其大小为总节点数减去当前节点的总子树大小。
然后就能得到每个节点最大子树的节点数,取其中最小值即可。
整个算法只需要遍历一遍树,时间复杂度为 O(n)。
1 | int siz[N],wgt[N]; |
树的直径
基本概念
简单路径:路径上的所有点至多只访问了一次。
树的直径:树上最长的简单路径。
简单来说,定义一棵树 T=(V,E),其直径为 maxlen(u,v)(u,v∈V)。
树的直径有两种常用求法,分别是两次DFS以及树形DP。
两次DFS求直径
算法思想
①从任意一点P出发,通过DFS寻找离它最远的点Q。
②再次从点Q出发,通过DFS寻找离它最远的W。
③直径即为WQ。
1 |
|
树形DP
树形DP准确的说是一种DP的思想,将DP建立在树状结构的基础上。
平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向:
- 叶->根:在回溯的时候从叶子节点往上更新信息
- 根 - >叶:往往是在从叶往根dfs一遍之后(相当于预处理),再重新往下获取最后的答案。
不管是 从叶->根 还是 从 根 - >叶,两者都是根据需要采用,没有好坏高低之分。
以一道经典例题开始叙述罢
没有上司的舞会
题目描述
某大学有 \(n\) 个职员,编号为 \(1\ldots n\)。
他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。
现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 \(r_i\),但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。
所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
输入格式
输入的第一行是一个整数 \(n\)。
第 \(2\) 到第 \((n + 1)\) 行,每行一个整数,第 \((i+1)\) 行的整数表示 \(i\) 号职员的快乐指数 \(r_i\)。
第 \((n + 2)\) 到第 \(2n\) 行,每行输入一对整数 \(l, k\),代表 \(k\) 是 \(l\) 的直接上司。
输出格式
输出一行一个整数代表最大的快乐指数。
样例 #1
样例输入 #1
1 | 7 |
样例输出 #1
1 | 5 |
提示
数据规模与约定
对于 \(100\%\) 的数据,保证 \(1\leq n \leq 6 \times 10^3\),\(-128 \leq r_i\leq 127\),\(1 \leq l, k \leq n\),且给出的关系一定是一棵树。
1 | 经典的树形dp |
1 | //没有上司的舞会 |