点分治【算法简介】
点分治是选择一个点将无根树变成有根树,然后递归处理每一棵以根节点的儿子为根的子树
为了优化时间复杂度,我们将重心作为选择的点,这样可以证明递归的深度最坏为O(logn),在链上取到最值
然后我们就需要考虑对于当前的重心u,如何计算贡献,一般来说,我们solve到每一个节点的时候,计算的是跨过这个点的来自两个子树的带来的贡献
有如下的计算方法:
1.总答案-不符合条件的答案
具体的,就是先计算一个点所有子节点之间的贡献,然后再减去来自同一个子树的部分即可
2.用当前子树和之前走过的部分来计算答案(更常用)
每次走完一个子树把有用的信息存下来,然后在走下一个子树的时候计算和记录的之前的信息贡献即可
具体的实现就看看题目的代码吧
【例题1】poj1741 Tree
求树上长度小于等于k的路径条数 sol.
【练习1】bzoj2152 聪聪可可
求树上长度为3的倍数的路径数 sol.
【例题2】bzoj2599 Race
求树上权值和为k的路径包含的最少边数 sol.
【习题1】bzoj4738 汽水
求一条路径,平均值最接近k sol.
【习题2】bzoj4016 [FJOI2014]最短路径树问题
在最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?sol.
【习题3】bzoj4182 Shopping
点分治+dfs序+多重背包dp(单调队列优化)sol.
动态点分治【算法简介】
动态点分治就是对于一些信息可以进行修改,仍然是要去求一些和路径相关的答案
我们可以利用一些数据结构去实现较为快速的查询和修改,然后再通过点分树来降低修改和查询的次数到log级别
那么什么是点分树呢?或许它可以算一种数据结构,十分小清新
点分树顾名思义,就是在点分治过程中生成的一棵树,我们只需要把新的计算出的root和之前的一个root(也就是当前solve的u节点)连上边,然后把子树的信息记录在当前的点里
这样我们就得到了一棵深度为logn的树,可以用于实现树上的一些问题了
但是这样就够了么? 考虑我们计算贡献的时候,会算重复一些东西
看一个例子:我们计算到u点距离不超过k的点权和,对于当前一个点u可以通过记录的子树信息算出距离为k的部分,但是u在点分树上父亲的部分就没有计算到
所以我们要不断的向上跳fa,然后计算fa记录的距离为k-dis(fa,u)的部分不断累加
但是这样我们就算重了,u子树内也可能存在距离fa为d-dis(fa,u)的节点
所以对于每个点u,我们要记录的是到u的距离为i的和sum[u][i],以及到u的fa的距离为i的部分sumfa[u][i]
这时候就需要利用一些数据结构去维护,比如线段树啊,树状数组之类的
由于每个点可能都需要记录这样的信息,所以一般都会用到动态开点和标记可持久化的技术
点分树
【例题1】P3345 [ZJOI2015]幻想乡战略游戏
给定一个树,要求如下操作
每次修改一个点权之后,询问选择树上哪一个点作为补给站u可以使dis(u,i)*vi的和最小 sol.
点分树+线段树/树状数组
【例题2】P6329 【模板】点分树 | 震波
给一个树,要求你支持如下操作
1.修改某个点的点权 2.询问到某个点的距离小于等于k的点权和 sol.
【习题2】BZOJ4372烁烁的游戏
和上面的例题十分相似,给一个树,要求你支持如下操作
1.查询某个点权 2.修改到某个点距离小于等于k的点 sol.
点分树+带删除的优先级队列
第一次写待删除的优先级队列
【例题3】P2056 [ZJOI2007]捉迷藏
给一个树,初始点权全部为0,要求你支持如下操作
1.把一个点的点权异或上1 2.查询树上点权为0的两点之间距离最大的距离 sol.