zoukankan      html  css  js  c++  java
  • 【算法】树

    ★ 无向无环连通图=树

    树上路径问题除了考虑树链剖分,还可以考虑离线树上差分。

    树上路径差分:x到根+y到根-lca(x,y)到根+fa[lca(x,y)]到根

    【最近公共祖先(LCA)】

    http://blog.csdn.net/wendavidoi/article/details/50670052

    void dfs(int x,int fa)
    {
        for(int i=1;(1<<i)<=deep[x];i++)
            f[x][i]=f[f[x][i-1]][i-1];
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa)
        {
            int y=e[i].v;
            f[y][0]=x;
            deep[y]=deep[x]+1;
            dfs(y,x);
        }
    }
    int find(int x,int y)
    {
        if(deep[x]<deep[y])swap(x,y);
        int d=deep[x]-deep[y]; 
        for(int i=0;(1<<i)<=deep[x];i++)
            if((1<<i)&d)x=f[x][i];
        if(x==y)return x;
        for(int i=20;i>=0;i--)
        if((1<<i)<=deep[x]&&f[x][i]!=f[y][i])
        {
            x=f[x][i];y=f[y][i];
        }
        return f[x][0];
    }
    倍增求LCA

    无向边建两条,数组开大!

    倍增数组的构造可以独立出来,只要第一重循环是倍增倍数就可以保证要用的已经算过。

    事实证明,简单的路上路径问题用倍增比链剖更有优势。

    性质:LCA其实就是两点到达根节点的路径的最近交点。

    性质:树上n个点至多有n-1个LCA,就是每两个dfs序相邻点的LCA。

    【树链剖分】

    http://blog.csdn.net/y990041769/article/details/40348013

    http://blog.csdn.net/acdreamers/article/details/10591443

    为什么要把节点数多的子树作为重链?因为对于同一根,重链查询最快,而轻链必须跳一步才能到重链,这就最大程度减少跳跃次数。

    假设最左端叶子节点要查询1次,则必须树大小为1*2+1=3;查询两次,树大小为3*2+1=7;也就是说对于节点数n的树剖分后查询的跳跃次数至多为log(n)次。

    不过,如果要把链再放到线段树处理就是O(log2(n))了。

    (模板)【BZOJ】1036 [ZJOI2008]树的统计Count

    void dfs1(int x,int fa)
    {
        size[x]=1;
        for(int i=first[x];i;i=e[i].from)
         if(e[i].v!=fa)
          {
              int y=e[i].v;
              deep[y]=deep[x]+1;
              fa[y]=x;
              dfs1(y,x);
              size[x]+=size[y];
          }
    }
    void dfs2(int x,int tp,int fa)
    {
        int k=0;
        pos[x]=++dfsnum;
        top[x]=tp;
        for(int i=first[x];i;i=e[i].from)
         if(e[i].v!=fa&&size[e[i].v]>size[k])k=e[i].v;
        if(k==0)return;
        dfs2(k,tp,x);
        for(int i=first[x];i;i=e[i].from)
         if(e[i].v!=fa&&e[i].v!=k)dfs2(e[i].v,e[i].v,x);
    }
    int find(int x,int y)
    {
        int sum=0;
        while(top[x]!=top[y])//while不是if
         {
             if(deep[top[x]]<deep[top[y]])swap(x,y)//比较顶点深度
            sum+=seg_sum(1,pos[top[x]],pos[x]);
            x=fa[top[x]];
         }
        if(pos[x]>pos[y])swap(x,y);
        lca=x;
        sum+=seg_sum(1,pos[x],pos[y]);
        return sum;
    }
    View Code

    过程:

    <dfs1>计算子树节点数,同时记录深度和父亲。

    <dfs2>分配pos编号并计算top链

    <solve>top不同时深度大的往上靠(注意是比较top的深度!注意用while不是if!),同top后pos

    求LCA:询问时u,v往同一条重链靠近,到达同一条重链时,pos较小的就是LCA。

    【树的直径】

    树上简单最长路,树上最远点对。

    证明:树的直径(最长路) 的详细证明

    求树的直径:①从任意点BFS找到树的直径一端,再从该点BFS找到直径。②DP记录最深和次深,组合比较最长路。

    拓展:树的直径相当于选择整棵树为点集的树上最远点对,故选定点集的树上最远点对也有以下性质。

    性质:距离树上任意点的最远的点一定是直径的一端(因为直径的证明没有涉及选定点本身,所以任意点可以是点集外的点)。

    【树上所有点的最远点】<1>第一次tree dp,得出f[i]表示i为根的子树的最深叶子路径,g[i]表示次深叶子路径。<2>第二次treedp,对于节点x,传下来s表示向上的最长路,所以s[x]=max(s,f[x])。遍历x儿子son[x]时s的传递:若f[son[x]]+1=f[x]则s=g[x]+2否则s=f[x]+2。

    【树的重心】

    引用自:关于树的重心的自我理解

    重心的三个等价定义:

    <1>最大的子树节点数最小

    <2>子树节点数均<=sum/2(否则往子树移动,根往上这棵树更小)(当存在子树节点数为sum/2时,该子节点也为重心,即双重心)

    <3>★所有点到重心的距离和最小

    树形DP求重心:令d[i]为节点数,则d[i]=∑[j]+1,对于不同节点i,比较所有节点的max(d[j],n-d[i])的大小。

    重心也可以基于点权定义,也就是d[i]=∑d[j]+w[i],定义sum=∑w[i],比较所有节点的max(d[j],sum-d[i])。

    ★例题:【BZOJ】3302: [Shoi2005]树的双中心 && 2103: Fire 消防站 && 2447: 消防站

    【虚树】

    当询问总数有限制时,我们可以对每个询问建虚树,每次复杂度为O(ki),总复杂度O(Σki)。

    虚树中保留特殊点和之间的LCA,根据k个点的相互LCA集合是按dfs排序后所有相邻点的LCA,可以快速求出虚树中的点。

    然后按dfs序入栈,若栈顶不是当前点的祖先则弹出栈顶,若是则连边,如此可以快速建出虚树。

    注意:垃圾回收保证复杂度。特殊点数组开两倍以存多余的LCA。

    例题:【BZOJ】2286: [Sdoi2011]消耗战

  • 相关阅读:
    Oracle SQL部分练习题
    Oracle 数据库和监听器开机自启动两种实现方法
    用Python连接SQLServer抓取分析数据、监控 (pymssql)
    Linux6.5 安装Python3.X(转载)
    SQLServer xp_instance_regread returned error 5,Access is denied(配置最小权限)
    [MySQL]存储过程
    [MySQL]触发器
    Linux 修改IP地址
    MySQL: InnoDB存储引擎
    mysql 重新添加主节点 (GTID)
  • 原文地址:https://www.cnblogs.com/onioncyc/p/6207462.html
Copyright © 2011-2022 走看看