MnZn 的树上问题总结

0./前言

MnZn 这个寒假学了新的树上问题算法,包括树链剖分, dsu on tree,静态&动态点分治,以及没了。感觉很好玩的同时有一些写题中的想法特意记录在此

1./树上启发式合并

可以优雅而较为高效地离线处理子树内的信息。可以先考虑在一棵子树内怎样统计答案,然后推广到整棵树上。

路径信息一般是以子树的根作为路径的 lca

时间复杂度证明:

众所周不知,树上一条链的重链数量是 log 级的。在 dsu on tree 的过程中,每个点在重链上会插入、删除各一次,所以复杂度是 O(n log 2n) ,但因为使用 map 统计信息所以可能常数较大

伪码
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 维护子树内部每个颜色的出现次数,并额外用 ans 记录最多次数的颜色的编号即可

1.2 给定路径权值和求最短路径长度

在 map 中以到根的距离作下标,维护相应距离的点的深度最小值(固定 lca 与一个端点的情况下,另一个路径端点深度越小路径长度越小),套用距离公式计算即可

1.3 给出若干路径求对每个点的贡献

将每条路径分为两半并求出相应的深度与贡献的关系,在此题中需用两个 map 维护起点/终点的信息。因为计算贡献需不重不漏,一条路径只能对一个点贡献一次,所以可以使用树上差分的插入方法:给起点插入次“询问”,终点插入次“询问”,lca 处减去次“询问”,lca 的父节点处减去最后的询问

2./点分治

适合处理大规模的树上路径统计问题

时间复杂度证明:

因为每次都以重心为根,分到子树内部时子树大小至少减少一半,所以点分治的递归深度是 log 级的。每个点会被分治过程中的 log 个子树统计到,所以总复杂度是 O(n log n)

2.1 给定路径长度判断是否存在

每个点都 get_dis 一下,再用双指针暴力判断即可

2.2 给出终点确定起点使得距离和最小

首先肯定和重心有关。考虑在什么样的情况下转移重心可以使得方案更优,并转移。因为在子树中找重心最多 log 次,所以总复杂度是 O(n log n)。尽管这样的复杂度很美丽,但还是不能不顾一切转移到底(这题卡常似乎无前途),当目前方案无法最优时直接输出即可。

3./点分树

若带修查询/强制在线,静态点分治就不够优秀了。考虑在点分治的递归过程中,一个点最多被 log 个子树包含计算,修改也只会影响这 log 个子树。所以可以根据点分治过程中的重心建树,在点分树中,一个节点的子节点是 点分治过程中该节点为重心的树的子树的重心,其深度和点分治的递归深度一样是 log 级的。

一般点分树上每个节点会用一个数据结构维护它的树的信息,每次修改只需要修改这个节点在点分树上的各级祖先维护的信息(所以点分树只需要记录每个点的父节点,不需要建完树)。因为这些树存在包含关系,为了统计得不重不漏,还需要再用一个数据结构维护它的树和它在点分树上的父节点的信息(一般不能不建第二个,因为它的树和它点分树上的父节点可能八竿子打不着)。每次修改是 log×数据结构复杂度

3.1 求给出距离内的价值和

在每个节点处,用一个线段树维护每个距离的价值和,然后直接查查完了(需再建一棵线段树去重)

3.2 同色点最长路径

用堆维护最大值&次大值,然后直接算算完了

trick:在堆内可以实现“修改”。将修改掰成“删除”和“插入”,插入好说,删除操作需额外开个堆记录删除的元素,每次查询前先把位于堆顶需删除的元素 pop 掉就好了

posted @   还是沄沄沄  阅读(25)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示