算法与数据结构八日谈之四——树论

1.树的定义

父节点、子节点、子树、祖先、后代、兄弟、根节点、叶节点、路径等

2.生成树

生成树的计数

Matrix-Tree定理

最小(大)生成树

-Prim算法
-Kruskal算法
部分维护动态图的题目可通过离线处理转化为维护最小生成树

3.树的重心

定义(性质)1:到树上所有点的距离和最小的点

定义(性质)2:割掉该点后最大的子树大小最小的点

-可通过递归处理树的重心来完成动态树分治
加权重心与平权重心求法类似

4.树的直径

顾名思义,树上最长的路径
简单的求法:先从一点求出最远点,再以该点求出其最远点,则这两点之间的路径为一条直径

树的中心

树的中心为直径的中点(不一定在某个节点上,也可能在某条边上,如果在某条边上,则所有直径经过该边)

5.最近公共祖先

基于倍增的lca算法,分别求出每个点的 2i 祖先, O(n) 时间可以求出 2i+1 祖先。

利用lca求出树上两点距离

分别求出每个点到根的距离,则两点距离为改距离和减去两倍lca到根的距离。

6.树的计数

由Matrix-Tree原理得: n 个点的无向完全图的生成树个数为 nn2
n 个结点的二叉树的个数为卡特兰数列的第 n

有度数限制的树的计数问题

7.树上的统计问题

静态树

-维护树的dfs序
-树链剖分
可以处理子树修改问题
-点分治
-边分治
-树分块

动态树

-Link-Cut-Tree
涉及到子树修改的则需要使用Top-Tree(然而不会写)

8.树的扩展

基环+外向树

先找出基环再对树进行处理,多数情况下需要删掉基环上的边以转化为树

仙人掌

每条边最多属于一个环的无向联通图
可以在树的算法上进行一定的扩展来处理仙人掌

下面是部分算法的代码实现

#include 

using namespace std;

typedef long long ll;
typedef double db;

const int inf=0x3f3f3f3f;

int getint()
{
    int f=1,g=0;char c=getchar();
    while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0' && c<='9')g=(g<<3)+(g<<1)+c-'0',c=getchar();
    return f*g;
}

namespace Tree_Theory{

    namespace Link_Cut_Tree{
        //bzoj某题 
        const int maxn=100005;
        const int mod=51061;

        typedef unsigned int uint;

        #define lc ch[x][0]
        #define rc ch[x][1]

        int n;
        int ch[maxn][2];
        int pre[maxn];
        int size[maxn];
        bool root[maxn];
        bool rev[maxn];
        uint sum[maxn];
        uint val[maxn];
        uint at[maxn];//at for Add Tags
        uint mt[maxn];//mt for Multiply Tags 


        void modify(int x,int m,int a)
        {
            if(!x)return;
            val[x]=(val[x]*m+a)%mod;
            sum[x]=(sum[x]*m+a*size[x])%mod;
            at[x]=(at[x]*m+a)%mod;
            mt[x]=(mt[x]*m)%mod;
        }

        void up(int x)
        {
            if(!x)return;
            sum[x]=(sum[lc]+sum[rc]+val[x])%mod;
            size[x]=(size[lc]+size[rc]+1)%mod;
        } 

        void push_down(int x)
        {
            if(rev[x])
            { 
                rev[lc]^=1;
                rev[rc]^=1;
                swap(lc,rc);
                rev[x]=0;
            }
            int m=mt[x];
            int a=at[x];
            mt[x]=1;
            at[x]=0;
            if(m!=1 || a!=0)
            {
                modify(lc,m,a);
                modify(rc,m,a);
            }
        }

        void rotate(int x)
        {
            int y=pre[x];
            int f=(ch[y][0]==x);

            push_down(y);
            push_down(x);

            pre[x]=pre[y];
            ch[y][!f]=ch[x][f];
            pre[ch[x][f]]=y;

            if(root[y])
            {
                root[y]=false;
                root[x]=true;
            }
            else
            {
                ch[pre[y]][ch[pre[y]][1]==y]=x;
            }
            pre[y]=x;
            ch[x][f]=y;

            up(y);
            up(x);
        }

        void splay(int x)
        {
            push_down(x);
            while(!root[x])
            {
                int y=pre[x],z=pre[y];
                push_down(z);
                push_down(y);
                push_down(x);
                if(root[y])
                {
                    rotate(x);
                }
                else
                {
                    int f=(ch[z][0]==y);
                    if(ch[y][f]==x)rotate(x);
                    else rotate(y);
                    rotate(x);
                }       
            }
            up(x);
        }

        int access(int x)
        {
            int t=0;
            for(;x;x=pre[t=x])
            {
                splay(x);
                root[rc]=true;
                root[rc=t]=false;
                up(x);
            }
            return t;
        }

        bool check(int x,int y)
        {
            while(pre[x])x=pre[x];
            while(pre[y])y=pre[y];
            return x==y;
        }

        void make_root(int x)
        {
            access(x);
            splay(x);
            rev[x]^=1;
        }

        void split(int x,int y)//提取从x~y的路径 
        {
            make_root(y);
            access(x);
            splay(x);
        }

        void Link(int x,int y)
        {
            make_root(x);
            pre[x]=y;
        }

        void Cut(int x,int y)
        {
            make_root(x);
            splay(y);
            pre[ch[y][0]]=pre[y];
            pre[y]=0;
            root[ch[y][0]]=true;
            ch[y][0]=0;
        }
        #undef lc
        #undef rc
    }
    namespace Tree_Chain{
        //bzoj1036

        const int maxn=100005;

        struct treenode{
            int l,r,sum,max;
        }a[maxn<<2];
        int tree[maxn],pre[maxn],son[maxn];
        int f[maxn],data[maxn];
        int size[maxn],top[maxn],deep[maxn];
        vector<int> g[maxn];
        int n,i,x,y,cnt,q;
        char c[10];

        int getint()
        {
            char c=getchar();
            int num=0,f=1;
            while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
            while(c<='9' && c>='0')num=num*10+c-'0',c=getchar();
            return num*f;
        }

        void add(int u,int v)
        {
            g[u].push_back(v);
        }

        void dfs(int k,int father,int d)
        {
            deep[k]=d;f[k]=father;size[k]=1;
            for (int i=0;iint x=g[k][i];if (x==father) continue;
                dfs(x,k,d+1);
                size[k]+=size[x];
                if (!son[k] || size[x] > size[son[k]]) son[k]=x;
            }
        }
        void dfs2(int k,int ancestor)
        {
            top[k]=ancestor;tree[k]=++cnt; 
            pre[tree[k]]=k;               
            if (!son[k]) return;        
            dfs2(son[k],ancestor);       
            for (int i=0;iint go=g[k][i];
                if (go!=son[k]&&go!=f[k]) dfs2(go,go);  
            }
        }
        void build(int k,int l,int r) 
        {
            a[k].l=l;a[k].r=r;
            if(l==r)
            {
                a[k].sum=a[k].max=data[pre[l]];
                return;
            }
            int mid=(l+r)>>1;
            int lc=(k<<1);
            int rc=lc+1;
            build(lc,l,mid);
            build(rc,mid+1,r);
            a[k].sum=a[lc].sum+a[rc].sum;
            a[k].max=max(a[lc].max,a[rc].max);
        }
        void update(int k,int x,int v)
        {
            if (a[k].l==a[k].r) 
            {
                a[k].sum+=v;
                a[k].max+=v;
                return;
            }
            int mid=(a[k].l+a[k].r)>>1;
            int lc=(k<<1);
            int rc=lc+1;
            if (x<=mid) 
                update(lc,x,v);
            else 
                update(rc,x,v);
            a[k].sum=a[lc].sum+a[rc].sum;
            a[k].max=max(a[lc].max,a[rc].max);
        }
        int sum(int k,int x,int y)
        {
            if (a[k].l>=x && a[k].r<=y) 
            {
                return a[k].sum;
            }
            int mid=(a[k].l+a[k].r)>>1,ans=0;
            int lc=(k<<1);
            int rc=lc+1;
            if (x<=mid) 
                ans+=sum(lc,x,y);
            if (y>mid) 
                ans+=sum(rc,x,y);
            a[k].sum=a[lc].sum+a[rc].sum;
            a[k].max=max(a[lc].max,a[rc].max);
            return ans;
        }
        int maxnum(int k,int x,int y)
        {
            if (a[k].l>=x && a[k].r<=y)
            {
                return a[k].max;
            }
            int mid=(a[k].l+a[k].r)>>1,o=-inf;
            if (x<=mid) o=maxnum(k<<1,x,y);
            if (y>mid) o=max(o,maxnum((k<<1)+1,x,y));
            a[k].sum=a[k<<1].sum+a[(k<<1)+1].sum;
            a[k].max=max(a[k<<1].max,a[(k<<1)+1].max);
            return o;
        }
        int askmax(int x,int y)
        {
            int f1=top[x],f2=top[y],t,ans=-inf;
            while(f1!=f2)
            {
                if (deep[f1]1,tree[f1],tree[x]));
                x=f[f1];f1=top[x];
            }
            ans= max(ans,(deep[x]>deep[y]) ? maxnum(1,tree[y],tree[x]) : maxnum(1,tree[x],tree[y]));
            return ans;
        }
        int asksum(int x,int y)
        {
            int f1=top[x],f2=top[y],t,ans=0;
            while (f1!=f2)
            {
                if (deep[f1]sum(1,tree[f1],tree[x]);
                x=f[f1];f1=top[x];
            }
            ans += (deep[x]>deep[y]) ? sum(1,tree[y],tree[x]) : sum(1,tree[x],tree[y]);
            return ans;
        }
    }
}

int main()
{
    return 0;
}

你可能感兴趣的:(图论,总结,树)