MnZn 的树上问题总结
0./前言
MnZn 这个寒假学了新的树上问题算法,包括树链剖分, dsu on tree,静态&动态点分治,以及没了。感觉很好玩的同时有一些写题中的想法特意记录在此
1./树上启发式合并
可以优雅而较为高效地离线处理子树内的信息。可以先考虑在一棵子树内怎样统计答案,然后推广到整棵树上。
路径信息一般是以子树的根作为路径的 lca
时间复杂度证明:
众所周不知,树上一条链的重链数量是
伪码
void solve(int x,int flag)
{
for (枚举轻儿子) solve(v,0)
solve(重儿子,1)//此时重子树的信息未被删去
for(枚举轻儿子)
{
for (枚举轻儿子子树节点) calc()//计算贡献
for (枚举轻儿子子树节点) add()//计入 注意要计算完贡献再加点 以防子树内部自己匹配
}
add(),calc()//必要时再计入 x 节点、计算 x 节点贡献
if (!flag) del()//若是轻儿子,删去贡献
//其实最后的“删去”也可以直接mp.clear(),但这里一个个点删和整体二分里一个个点删的道理一样
}
1.1 子树数颜色
用 map 维护子树内部每个颜色的出现次数,并额外用
1.2 给定路径权值和求最短路径长度
在 map 中以到根的距离作下标,维护相应距离的点的深度最小值(固定 lca 与一个端点的情况下,另一个路径端点深度越小路径长度越小),套用距离公式计算即可
1.3 给出若干路径求对每个点的贡献
将每条路径分为两半并求出相应的深度与贡献的关系,在此题中需用两个 map 维护起点/终点的信息。因为计算贡献需不重不漏,一条路径只能对一个点贡献一次,所以可以使用树上差分的插入方法:给起点插入次“询问”,终点插入次“询问”,lca 处减去次“询问”,lca 的父节点处减去最后的询问
2./点分治
适合处理大规模的树上路径统计问题
时间复杂度证明:
因为每次都以重心为根,分到子树内部时子树大小至少减少一半,所以点分治的递归深度是
2.1 给定路径长度判断是否存在
每个点都 get_dis 一下,再用双指针暴力判断即可
2.2 给出终点确定起点使得距离和最小
首先肯定和重心有关。考虑在什么样的情况下转移重心可以使得方案更优,并转移。因为在子树中找重心最多
3./点分树
若带修查询/强制在线,静态点分治就不够优秀了。考虑在点分治的递归过程中,一个点最多被
一般点分树上每个节点会用一个数据结构维护它的树的信息,每次修改只需要修改这个节点在点分树上的各级祖先维护的信息(所以点分树只需要记录每个点的父节点,不需要建完树)。因为这些树存在包含关系,为了统计得不重不漏,还需要再用一个数据结构维护它的树和它在点分树上的父节点的信息(一般不能不建第二个,因为它的树和它点分树上的父节点可能八竿子打不着)。每次修改是
3.1 求给出距离内的价值和
在每个节点处,用一个线段树维护每个距离的价值和,然后直接查查完了(需再建一棵线段树去重)
3.2 同色点最长路径
用堆维护最大值&次大值,然后直接算算完了
trick:在堆内可以实现“修改”。将修改掰成“删除”和“插入”,插入好说,删除操作需额外开个堆记录删除的元素,每次查询前先把位于堆顶需删除的元素 pop 掉就好了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】