基环树笔记
LeavingZzz · · 算法·理论
update:“基本定义以及方法”这一节中出现的对基环树的特点描述中出现了较为严重的错误,对于一个
对于博客的内容出现问题表示万分抱歉,笔者水平有限,如果还有相关问题请立即提出,谢谢各位/qq
基环树
啥啥啥鸡排?
是无向树哇/youl
前置技能:
树的直径、最大独立集等有关树形DP的经典问题
输入:
解法
首先我们知道,全部的方案数是
环外的边无论正向还是反向都无所谓,既不会影响已有的环,也不会增加环。
但是环内的任意一条边只要方向和其他的边不一样,就没救了。
所以我们发现在所有的方案中,只有环内边方向全部为正以及环内边方向全部为反两种情况是非法的,这时候非法方案数为
并不是(
因为题目并没有保证图是联通的,所以应该对于整个图找到所有的基环树的环,每一个基环树可以向上面描述的那样计算,答案为
每次输入一条边的时候就把边的端点用并查集合并到同一个联通块里,当发现一条边的端点已经在同一个联通块内的时候,这条边为一个环上的边,而对于一棵基环树,这样的边只会出现一次,因此可以存下来方便之后处理
P1453 城市环路
题意
给出一个
解法
没错,就是方法2了
去掉多出来的一条边之后问题就变成了树的最大独立集问题,是个板子就不多赘述
问题是现在那条边还是连在图里的
假如我们已经把这条多余的边的两个端点找了出来,设为
我们可以把这三种情况都计算出来,先强制不选取
现在是小细节部分
-
- 强制选取的方法,我是暂时将节点的权值设为
-\infty 来禁止选取该节点(要记得设为-\infty 后一定要换回来)
于是我们就解决了这题/cy
P2607 骑士
题意
有
解法
首先建模,相互厌恶的骑士之间连一条无向边,那么,答案就是最大独立集
熟悉的
还是求最大独立集,似乎刚刚做的城市环路照搬即可?
那当然不完全是(不然为啥这题是个紫题啊喂
因为本题的图可以不连通,但是每个连通块点数仍然等于边数,所以图会变成基环树森林
这里插说一句,本人不是很喜欢用 主要是因为太菜了不会/kk
这里仍然使用并查集辅助找环
这时候你也许会表示疑惑:刚刚的图是联通的所以可以用并查集找到多余的那条边,要不是联通的,环不止一个,多余的边也不止一条怎么办?
那就把所有的多余边的端点存下来/cy
然后对于每一个多余的边,它一定对应且只能对应一棵基环树,所以可以写函数直接计算单棵基环树的最大独立集,最后答案累加即可。
就将其当成若干个城市环路解决就行了。
P4381 [IOI2008]]Island
题意
这题考语文((
主要是对坐渡船的理解,这里坐渡船的条件是不能有桥直接连接坐船的起止点,也不能在一个地方反复坐船
对坐船次数实际上没有限制
每个点只能经过一次,要求最长的在桥上走的路程。(即求最长的一条路径)
解法
这就引入了一个很重要的问题——基环树的直径问题
基环树的直径是指基环树上任意两点间距离的最大值
本题求的是最长链,但是方法和直径差不多
(这道题目图不连通还是求最长链,因为坐船次数不受限,给出的如果是一个基环树森林我们就把每一棵基环树都求一个最长链,最后直接加和即可,因为我们可以走完一棵基环树的最长链再坐船去另外一棵基环树再走最长链)
所以接下来我们只关注一棵基环树的最长链
按照下图考虑,图抽象成一个环上面挂一堆子树
考虑这个最长链的起止点
最长链的起止点一定是没有叶结点的,否则继续向下走直径还能继续变长。
所以我们可以对于环上的每一个节点都预处理一下它下挂的子树的最最深深度和这个子树的直径(有可能一个子树的直径就能成为整棵基环树的最长链)
然后给环上的每个节点带上权,权值为该节点下挂子树的最深深度(没有子树权值就为0)
接下来就要选择一对点
现在的难点就在于
对于环,我们有一个经典的方法叫断环成链
也就是将环倍长,
这样一条链上的选取方案就能包括原来环上的所有选取方案啦
加上链上的处理更简单,现在考虑
我们从最左边的权值为
这是啥?
滑动窗口嘛!
我们用单队维护最大的
代码细节注意:
- 过期的
j 的范围要注意 - 考虑一下自己的代码对像这样的二元环是否有抵抗力
- 找环、处理环上/环外节点信息是否写对(特别是一个环的端点是不是处理到了)
另外提一句,这样的题目尽量不要为了只是少写一个函数去强行把功能集成到一个函数上,这样造出来的代码不会好看,笔者认为,不好看的代码它效果也一定不会好,该重构的时候就重构
推荐一个功能写一个函数,比如本题的
由于这题细节还比较多,以及实现时经常 “写丑”,给一份代码,参考调试以及帮助不知道怎么实现好的同学。
(注释充足,代码....不敢评价成好看,但是绝对不丑QwQ)
我才不会告诉你这题代码写丑导致我114514了一下午
[NOI2013] 快餐店
题意
给出一个
解法
首先证明,答案即为基环树的直径的一半
假设中点落在橙色的节点上,红色的路径为直径,那么一定不存在节点到橙色节点距离比直径的两个蓝色端点更远。
否则,红色路径就不是直径了,这时候直径就变成了一半红色路径和那个离得更远的点组成的路径
至于为什么上面的图画的是个树不是基环树,基环树的直径一定是不会经过环上的所有边的
如图,最多经过
所以把基环树环上的边可以断掉一条,这样他就是棵树了/cy
对于树我们是有比较好的解决方法的,直接求树的直径相信各位都会了,所以现在就有一个相当暴力的方法就是枚举环上的边依次断掉,然后对生成的树做一次DP求出直径,在所有直径中取一个最小值即可,时间复杂度是
那么,既然图是一棵基环树,有什么更好的方法么?
当然有
正如
(搬图)
你会发现在断出来的链上的任意的连续
那么能不能快速的统计这
设数组
那么假设由点
所以我们的目的就是在当前选取的
但是注意这里一定要满足
因此,我们不能仅仅维护
仍然可以使用单调队列,但是细节很多实现并不是很方便,可以考虑使用优先队列(堆),虽然这样会让时间复杂度带上一个
还要注意从基环树将子树收缩到环上的时候要计算子树内部的直径,这也是可能的答案。
总结
好啦,题目也讲了不少了,现在你对基环树的理解应该不少啦,解题方法大多都跑不出开头提到的两种方法:忽略多出来的一条边 或者 抽出环上挂一堆子树然后把图降级到环最后降级到链。
练习题
CF835F
P2081