zoukankan      html  css  js  c++  java
  • 浅谈算法——点分治

    很多情况下,我们会遇到在树上进行操作的题目,比如求树上路径长度为(k)的路径条数

    大力枚举?每次枚举两个点暴力算距离,时间复杂度(O(n^3)),高级数结维护一下(O(n^2log n)),emmmm……,都TM高级数结维护都跑不了1w的数据,(╯°Д°)╯︵ ┻━┻

    大力枚举肯定不行了,我们得换个想法,我们考虑一下每条路径,假如一个满足条件的路径经过(x),那么这条路径要么以(x)为端点,要么两个端点在(x)的不同子树内

    那么,只要我们找到(x),就可以统计这样的一些路径了,然后……如何找到(x)呢?

    首先明白,(x)是不能随便找的,为了保证时间复杂度,(x)显然为重心最优,找重心可以用一个简单的dfs搞定

    (x)统计完了之后呢?这棵树被分成了两个大小基本相同的子树,然后我们把子树剥离(记得是剥离)出来,作为一个子问题继续处理,这次的处理是没必要让路径经过(x)的(因为经过(x)的已经被统计完了),以此类推下去,我们发现每个点在统计答案的时候最多被经过(log n)次,因此时间复杂度为(O(nlog n))

    好,然后我们找到(x)了,然后我们得到一堆以(x)为端点的边,然后满足条件的路径要么是以(x)为端点,要么是两条以(x)为端点的路径接到一起,如何统计?枚举,(O(n^2))……

    开个桶啊,(k)最大不会超过(n)(超过(n)直接输出0吧),对于每次分治找到的(x),只需要(O(size))扫一遍桶即可,复杂度依然保持在(O(nlog n))(不过你要是memset清空桶那我没点办法)

    然后这题就做完了,吗?有些有意思的情况诶,拼接起来的路径,要保证两端点分别在两个子树内,这好办,我们枚举分治重心的子树时,开个临时桶记录当前子树信息,在永久桶中统计答案,然后再把临时桶的信息加入永久桶中

    这道题这样子没错,其他的呢?比如说我要统计路径长度小于等于(k)的路径数咋办?这个时候桶就没啥用了,首先记(dis[i])表示分治过程中(i)(x)的距离,然后对(dis[])排个序,每当我们找到(l)后,可以找到一个最大的(r),满足(dis[A_l]+dis[A_r]leqslant r)(A)是排序后的序列),那么对答案的贡献即为(r-l+1)

    做完了?不,这样统计有点问题,答案好像多了点,如下图

    我如果要统计长度(leqslant 4)的路径,这样子不是把(A ightarrow x ightarrow B)给考虑了,但是,显然不对啊,这哪能叫(A ightarrow B)的简单路径啊。因此我们发现,我们将一些端点在同一颗子树内的路径拼接起来了!行吧,既然多统计了,我们减去它们即可

    如何减去?既然是端点同在一个子树内被统计了多次,我们就从子树这里下手

    我们可以令(son)的权值为1,再次访问这棵子树,这样统计出来的答案,必然在(x)统计答案时满足两个端点同在(son)的子树内,我们将这样统计出来的答案减去即可

    然后时间复杂度?肯定不是说桶排那个啊,那个显然(O(nlog n))没得谈,我说的是后面那个,显然要快排嘛,然后时间复杂度(O(nlog^2n))?但是实测起来貌似没有多多少啊?为啥?

    个人理解,树上路径条数总共(n^2)条,每次快排之后相当于消去那些边,就算需要容斥,每条边至多被排序2次,复杂度应该依然维持在(O(nlog n))级别,只是常数较大

    然后就愉快的切题去吧

  • 相关阅读:
    Android Shell命令dumpsys
    查看Android内存的8中方法
    Viewpager 的相关总结
    FileZilla等软件搭建ftp服务器
    Linux系统下邮件服务器的搭建(Postfix+Dovecot)
    PHP 打乱数组
    PHP 日期 加减 月数,天数,周数,小时,分,秒等等
    PHP 常用函数
    111
    phpStudy启动失败提示:缺少VC9运行库
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/10212857.html
Copyright © 2011-2022 走看看