点分治是一种树分治算法(昨天听了清华大佬Ryz的课,感觉这个人讲的还可以,虽然后面有蛮多没听懂,比如再套一些七里八里的数据结构)
在解决树上路径满足某种属性的数量统计方面有着很大的作用
点分治的基本思想如下:
考虑到树上的路径对于一个点来说只有两种情况:一是经过这个点,二是不经过这个点
对于不经过这个点的情况我们可以直接往下递归处理,主要问题就是解决经过这一个点的路径
我们知道:如果一条路径要经过这个点,那么他必然是由两条在这个点不同子树中到这个点的路径组合而成(这句话真的很重要)
对于这个问题一般又有两种思想:
1.运用容斥的思想,先把这个节点getdeep的时候搞出来的东西不管是否经过这个点(即不在同一子树)全部都计算,然后在减去子树中(即在同一子树)的这些答案
这样减剩下就是经过这个点的答案了(参考 树上的点对以及聪聪可可以及吕欣大爷的c题)
2.类似动态点分治的想法,我们直接对于每个点的子树的一个一个的做(组合或更新);比如说经常用一些堆,单调队列(貌似还有平衡树???)这些东西来实现子树贡献合并(参考所有动态点分治)
动态点分治:
相当把重心拿出来重新构了一个新的树型关系(可以理解为线段树,每个节点的性质真的跟线段树很像)
每个重心所管辖的就是他能getdeep的那一片连通块,然后每个重心额外记一个上层重心即可
这样有什么好处呢???
我们发现修改的话,这个被修改的点只会影响该点上层的所有重心(log个)
查询关于某个点的信息时,也只要不断的往上跳上层重心进行查询(log次)
那么这样的复杂度就很能够接受,至于答案的统计,我们就通过上面说的第二种统计答案的方法进行统计即可
具体实践参考 qtree5,开店,捉迷藏,重建计划等等,这一类问题统计答案的方法千变万化,就像线段树一样有很多不同的方法
所以一定要好好理解,然后灵活运用,点分治这个算法(or数据解构)其实真的挺有用的
再补充一下关于处理经过一个点的路径的降低转移复杂度的方法:
如果关于一个点的路径的统计是O(size)的那么复杂度是n*logn
如果关于一个点的路径的统计是O(log*size)的那么复杂度是n*logn^2
上两种复杂度都是可以接受的,如果再高的话就只得自求多福
有一些人生经验(世间万物皆套路...):
1.跟倍数统计相关:开桶
例:聪聪可可的桶和lx的c题的桶
2.如果发现主要是由于某个值的无序性导致无法确定某个值使得无法快速转移,那么就考虑能否sort后实现O(size)转移
例:树上的点对(权值和排序后线性扫),开店(按年龄排序后提取前缀和),吕欣的c题(按最大值排序后依次加入)
3.统计一些最值之类的东西:开堆(并考虑取最优和次优的组合之类的)
例:捉迷藏(取子树最优+次优合并),qtree5(堆维护最小值)
4.单调队列(做得少...)
例:重建计划(单调队列维护[L,R]间权值和最大的路)