zoukankan      html  css  js  c++  java
  • 树的直径,树的重心,树的分冶

    转载地址:http://blog.csdn.net/pi9nc/article/details/12394117

    主要是利用了反证法:

    假设 s-t这条路径为树的直径,或者称为树上的最长路

    现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路

    证明:

    1    设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则

    dis(u,T) >dis(u,s)     且  dis(u,T)>dis(u,t)   则最长路不是s-t了,与假设矛盾

    2   设u不为s-t路径上的点

        首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了

        所以现在又有两种情况了:

        1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)

        2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,

        则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾

        附上一张第二种情况的图

         

       

    分类: 图论

    树的“重心”的一些性质及动态维护  

    2011-08-24 20:31:13|  分类: 程序|字号 订阅

    还记得曾经提到过的树的“重心”吗?重心的定义是:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

    树的重心的一个的性质:
    树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
    这也是“道路修建”带来的启发。(证明:调整法)

    树的重心的另一个性质:
    把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
    这个让“重心”名副其实了。。。(证明:。。。自己好好思考一下吧。。。)

    还有一个性质:
    把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
    (证明:提示:张放想都没想说,要不然那两个不等式就矛盾了)

    嗯,不错,有这么多性质,可以出不少恶心题了。。。

    不过,我还是更关心一个事情:重心的动态维护。
    如何动态呢?
    情景1:添加一片叶子
    根据已有性质,添加一片叶子之后新的重心要么不动要么向那片叶子的方向移动一下。这样,可以用一个link-cut tree来维护。
    我们以重心为根建立这个动态树。每个节点维护它所在的子树的大小。添加叶子等于向一条路径上的维护值增加1,这个可以通过打标记实现。发现不得不移动的时候进行一次换根的操作。因为只可能移动1,所以换根的操作是可以完成的。
    我们甚至还可以维护所有点到重心的距离和!这个只需给每个点加一个维护值:这个点为根的子树中所有点到这个点的距离和,通过稍微有点复杂的标记系统还是可以维护的。

    情景2:删除一片叶子
    只有删除操作?那么离线改成添加吧。。。
    不允许离线?那么我们要换一个思路:
    定义稍微广义的树的重心:每个点有一个非负权,每个边有个正的长度,到所有点的权乘以距离的和最小的点定义为重心。
    注意:树的重心的位置和边的长度没有关系!。
    在只有权值修改的情况下,我们可以利用树分治配合基本数据结构来维护树的重心:
    注意到,我们可以维护一个子树内的点的权值和(利用dfs序)。这样给定一条边,我们就能够知道树的重心在这条边的哪边(看看哪边权值和大就行了)。
    这样,我们可以先找一个比较靠近中心的边,问问应该向哪边走,再分治下去,就像树分治那样(类似二分查找?)。
    当然,要想一下其他的技巧来对付”星型数据“,这个应该不难(通过拆点、拆边的技巧)。
    利用这个广义一点的重心,我们发现,删除操作其实就是把权修改成0而已,可以在log^2N的时间内动态维护了。
    如何处理多重心的情况?”抖动“一下权值使得只有一个重心不就行了。。。那另一个重心在哪里?这个只是个细节问题。。。
    能否维护距离和?能否在logN的时间内维护?欢迎讨论(将子树和查询与树分治结合起来?。。。)。

    情景3:移动一片叶子
    把一个叶子移动到另一个地方。
    这个怎么维护呢?其实,我们发现,新的重心应该在原来的重心和叶子新的位置的连线上(证明?应该是对的吧),移动距离很小。于是,也就可以维护了。

    情景4:移动一个子树(被移动的子树小于原树的一半,并且保持它的根不变)
    这个可以维护吗?
    新的重心在原来重心和新子树的接合点的连线上吗?(证出来的欢迎留言)
    有一个可以和link-cut tree配合使用的工具,叫做Euler-tour tree,它维护树的欧拉回路,基本元素是边。利用它,可以方便的完成子树的移动,并且给定一条有向边,可以回答这条边指向的子树的大小(Eurler-tour tree中没有”根“以及”父亲“这个概念!)。如果上面的那个论断是对的,那么这个应该可以维护了(复杂度?log^2N吧。。。)
    另一个思路是树块划分,把树划分成若干联通块,并且在查询的时候合并相邻的联通块使得每个联通块的大小都是sqrt(N)的级别。这个东西对维护是否有帮助?欢迎交流。。。

    情景5:开始N个一个点的树,每次用一条边合并两个树,要求回答新的树的重心
    离线?在线?logN?log^2N?sqrt(N)?
    等待你去探索

    POJ 1655 - DP 树的重心,经典 #P

    分类: #P DP 119人阅读 评论(0) 收藏 举报
    POJ 1655 - DP 树的重心,经典 #P
     
    题意:求树的重心。
    树的重心:删去重心后,生成的多棵树尽可能平衡。
    重心的意义,在对树进行分治的时候可以避免N^2的极端复杂度(从退化链的一端出发),保证NlogN的复杂度。
     
    解法:
    一开始想到的是模仿求树的直径那样子去Dp,两次DFS。
    son[i] - 结点i的儿子结点数目
    第一遍求出son;
    h[i] - 结点i向上的结点数目
    h[i] = h[k] + son[k] - son[i] - 1;
    blance = max(son[j] , h[i])
    第二遍求出h,和blance;
     
    后来去看题解,才发现有更简单的方法。
    应用一个性质,h[i] = n - son[i] -1;
    blance = max(son[j] , n - son[i] -1);
    这样只需要一次DFS。
     
     
  • 相关阅读:
    windows7上使用docker容器
    centos7 docker镜像加速器配置
    用列表生成器打印九九乘法表
    -bash: wget: command not found的两种解决方法
    centos7 Dockerfile安装nginx
    centos6.5关闭防火墙命令
    centos7开机启动tomcat7
    centos7安装tomcat7
    CentOS7防火墙firewalld
    poj_3662 最小化第k大的值
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410601.html
Copyright © 2011-2022 走看看