启发式合并
有一种暴力的想法是先 DFS 每个结点,再对每个结点 DFS 它的子树,用
一个平平无奇的优化:第一层 DFS 的时候,把重儿子放到最后搜索。在搜索重儿子的子树后,不清空
这个优化可以把算法变成
类比并查集的按秩合并优化。每次都是小的向大的连,复杂度是小集合的规模。这样每个小集合要做贡献,规模都至少翻倍,所以复杂度
这一题,每一个结点想要贡献复杂度,必须出现在某个轻儿子的子树内。而每个轻儿子的父结点的规模,至少是轻儿子规模的两倍。所以一个结点贡献的次数不会超过
观察到一个性质:
证明:若
令
发现
如果对每一个结点,都循环它的子结点,再循环它的子结点的
但是要启发式合并,好像也不太好搞啊?我们可以启发式和暴力,交叉着用。
-
如果
每个数只出现一次,用启发式合并。令 为重儿子,先令 ,然后再让 和其他的 合并。 -
如果出现次数最多的数,至少出现两次。可以暴力用
统计出出现次数最多的数。因为至少出现两次,所以 至多是 。
还有遗留两个问题:
-
如何判断是否每个数只出现一次?
我们可以先啥都不管,就先执行启发式合并的流程。等到
中发现一个数在当前 中已经有了,才进入第二种情况。这样做复杂度不会爆,因为至多也就是让复杂度加上
。 -
如何快速让
中的数异或上父结点的权值?我们可以设置一个偏移量,
中的数不是真实的让 最小的数,异或上偏移量才是。
点权的异或路径,先来一个经典 trick:求出每个点到根的异或是多少,记为
注意到可以改成任意整数。所以第
把一条异或为
证明:记这个 LCA 为
所以现在的任务就是快速求出最深的坏路径 LCA。但是如果每次求出所有 LCA,实在是太慢了。
可以转换一下思路:不去找所有 LCA,而是从深到浅枚举结点,判断一下这个点是否是坏路径的 LCA!
具体而言,对每个结点维护一个 set
:
当我们 DFS 到一个结点
这个时候我们已经得到了所有
如果不存在,令
直接这么做肯定 TLE,但是我们可以做一个优化:在算
这样一个
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!