zoukankan      html  css  js  c++  java
  • 树链剖分

      最近刚刚讲了树链剖(pōu)分,练了几道题,也该来个小结了。

      事实上,树链剖分就是把树切成几条链,再套上一个数据结构罢了。这样子有个好处,就是可以高效地完成在树上的一些操作。

    剖分的方法

    1. 随机剖分

    2. 盲目剖分

    3. 启发式剖分

    从名字上看,明显启发式剖分是最佳选择(= =)所谓启发式剖分,就是重链剖分。

    先看两个概念:

    重边:树中父节点连接儿子中size(即以它为根的子树的结点个数)最大的一条边。

    轻边:非重边。

    重链:全部由重边组成的一条链(包括一个点)。

    重链剖分就是将整颗树剖分成几条重链。

    (为什么这样最优?见1.)


    应用


    可以用来对一条路径上的一些信息进行操作,例如单点/区间修改,第k大的查询

    等,可以根据不同的操作,选择不同的数据结构(如线段树,平衡树)。



    缺点


    无法动态地删除/插入新的边。(如果想实现,可以用动态树)


    实现



    1. 重链剖分:


      这个有两种实现方法一种是套上一个数据结构,一种是套上几个数据结构。

      这里详细介绍下第一种(由cxjyxx_me大牛自创),第二种请见Xietutu's blog.

      先找到重边,过程中,如果确定出一条轻边就以其为根建成一条链,接着建成一个数据结构(例如线段树,平衡树),最后在来一次以1为根的build_chain即可。

      因此这样做会建出好几颗树。不过效率会提高许多。有图为证http://www.lydsy.com/JudgeOnline/problemstatus.php?id=2243&page=0


                  //伪代码 by Lazycal
      build_chain(x)
        k=0
        while exists x
          save the node x
          update x's chain_head
          update x's root // such as segment tree or balanced tree
          x=x's child which has maximal size
                                                                                                         
        build_tree // segment tree or balanced tree
      ========================================================
      dfs(k)
        for each edge (u,k) in V and not vis[u] 
        dfs(k);
        update k.size
        if u.size>max.size build_chain(max)
        else build_chain(u)
              



    2. 查询/修改


      对于u,v:

      1.当u与v在同一重链时,直接在数据结构里面查询/修改,break。

      2.当u与v不同重链时,先取出重链头比较靠下的(设为u),然后查询/修改u与u's chain_head, the father of u's chain_head->u。

      如此往复。


    1.证明如下:可以这么理解(个人浅见),设x为p的轻边所连的孩子,则x.size<p.size/2(为什么?请读者自己想想,很简单的),这样一来,任意一条链的轻边个数不超过log(n),所以重链数自然也不会超过log(n),于是u到v最坏情况是u走上去到某个点再折下来到v,但即使如此对于数据结构的询问次数也不超过log(n)次,而每次数据结构的询问时间为log(n),于是复杂度便可视为O(log(n)^2)。


    例题

    1.bzoj1036(模板)


    //linenums won't be copied

    #include <cstdio>
    #include <algorithm>
    const int N=30000+9;
    struct node
    {
        int lc,rc,max,sum,l,r;
        node (const int a=0,const int b=0,const int c=0,
              const int d=0,const int e=0,const int f=0):
            lc(a),rc(b),max(c),sum(d),l(e),r(f){}
    }a[N*4];
    struct Point
    {
        int seg_root,head,value,max,d,size,p;
    }p[N];
    struct info
    {
        int next,link;
    }es[N*2];
    int n,q,son[N],nt,ec,t[N];
    char s[10];
    inline int lef(int x){return a[x].lc;}
    inline int rig(int x){return a[x].rc;}
    void addedge(int x,int y)
    {
        es[++ec].next=son[x];
        son[x]=ec;
        es[ec].link=y;
    }
    int build_tree(int l,int r)
    {
        ++nt;
        if (l==r) {
            a[nt]=node(0,0,p[t[l]].value,p[t[l]].value,l,r);
            return nt;
        }
        int index=nt,lc=build_tree(l,(l+r)/2),
            rc=build_tree((l+r)/2+1,r);
        a[index]=node(lc,rc,std::max(a[lc].max,a[rc].max),a[lc].sum+a[rc].sum,l,r);
        return index;
    }
    void build_chain(int x)
    {
        int k=0,root=x;
        while (x) {
            p[x].seg_root=nt+1;
            p[x].head=root;
            t[++k]=x;
            x=p[x].max;
        }
        build_tree(1,k);
    }
    void tr(int x,int d,int fa)
    {
        p[x].d=d;p[x].size=1;
        for (int i=son[x];i;i=es[i].next)
            if (es[i].link!=fa) {
                int u=es[i].link;
                tr(u,d+1,x);
                p[u].p=x;
                p[x].size+=p[u].size;
                if (p[u].size>p[p[x].max].size) {
                    if (p[x].max) build_chain(p[x].max);
                    p[x].max=u;
                }else build_chain(u);
            }
    }
    void modify(int index,int v,int pos,int x)
    {
        int k=0;
        while (a[index].l!=a[index].r) {
            a[index].sum+=v-p[x].value;
            t[++k]=index;
            int mid=(a[index].l+a[index].r)>>1;
            if (pos<=mid) index=a[index].lc;
            else index=a[index].rc;
        }
        p[x].value=a[index].sum=a[index].max=v;
        while (k--) 
            a[t[k+1]].max=std::max(a[lef(t[k+1])].max,a[rig(t[k+1])].max);
    }
    void seg_query(int L,int R,int &res,int tag,int index)
    {
        int l=a[index].l,r=a[index].r,mid=(l+r)>>1;
        if (L<=l && r<=R) {
            res=tag?res+a[index].sum:std::max(a[index].max,res);
            return;
        }
        if (L<=mid) seg_query(L,R,res,tag,lef(index));
        if (mid< R) seg_query(L,R,res,tag,rig(index));
    }
    int query(int u,int v,int tag)
    {
        int res=tag?0:-0x7fffffff;
        while (p[u].head!=p[v].head) {
            if (p[p[u].head].d<p[p[v].head].d) std::swap(u,v);
            seg_query(1,p[u].d-p[p[u].head].d+1,res,tag,p[u].seg_root);
            u=p[p[u].head].p;
        }
        if (p[u].d>p[v].d) std::swap(u,v);
        seg_query(p[u].d-p[p[u].head].d+1,
                  p[v].d-p[p[v].head].d+1,
                  res,tag,p[u].seg_root);
        return res;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("1036.in","r",stdin);
        freopen("1036.out","w",stdout);
    #endif
        int x,y;
        scanf("%d",&n);
        for (int i=1;i<n;++i) {
            scanf("%d%d",&x,&y);
            addedge(x,y);addedge(y,x);
        }
        for (int i=1;i<=n;++i) scanf("%d",&p[i].value);
        tr(1,1,0);
        build_chain(1);
        scanf("%d\n",&q);
        while (q--) {
            scanf("%s",s);
            if (s[0]=='Q' && s[1]=='M') {
                scanf("%d%d\n",&x,&y);
                printf("%d\n",query(x,y,0));//0: max
            }else if (s[0]=='Q' && s[1]=='S') {
                scanf("%d%d",&x,&y);
                printf("%d\n",query(x,y,1));//1: sum
            }else {
                scanf("%d%d\n",&x,&y);
                modify(p[x].seg_root,y,p[x].d-p[p[x].head].d+1,x);
            }
        }
    }
    

    2.bzoj2243(模板,注意处理两条重链的连接处)

    //linenums won't be copied

    #include <cstdio>
    #include <algorithm>
    const int N=100000+9;
    struct point
    {
        int value,seg_root,head,max,size,d,p;
    }p[N];
    struct Triple
    {
        int num,lcol,rcol;
        Triple (const int a=0,const int b=0,const int c=0):
            num(a),lcol(b),rcol(c){}
    };
    struct node
    {
        int lc,rc,tag,lcol,rcol,num,l,r;
        node (const int a=0,const int b=0,const int c=0,const int d=0,
              const int e=0,const int f=0,const int g=0,const int h=0):
            lc(a),rc(b),tag(c),lcol(d),rcol(e),num(f),l(g),r(h){}
    }a[N*2];
    struct info
    {
        int next,link;
    }es[N*2];
    int n,m,nt,son[N],col,t[N],ec;
    inline int lef(int index) {return a[index].lc;}
    inline int rig(int index) {return a[index].rc;}
    inline int getindex(int u) {return p[u].d-p[p[u].head].d+1;}
    int build(int l,int r)
    {
        int index=++nt;
        if (l==r) a[index]=node(0,0,p[t[l]].value,p[t[l]].value,p[t[l]].value,1,l,r);
        else {
            int lc=build(l,(l+r)>>1),rc=build(((l+r)>>1)+1,r);
            a[index]=node(lc,rc,0,a[lc].lcol,a[rc].rcol,
                          a[lc].num+a[rc].num-(a[lc].rcol==a[rc].lcol),l,r);
        }
        return index;
    }
    void build_chain(int x)
    {
        int k=0,root=x;
        while (x) {
            p[x].seg_root=nt+1;
            p[x].head=root;
            t[++k]=x;
            x=p[x].max;
        }
        build(1,k);
    }
    void tr(int x,int d)
    {
        p[x].d=d; p[x].size=1;
        for (int i=son[x];i;i=es[i].next)
            if (es[i].link!=p[x].p) {
                int u=es[i].link;
                p[u].p=x;
                tr(u,d+1);
                p[x].size+=p[u].size;
                if (p[u].size>p[p[x].max].size) {
                    if (p[x].max) build_chain(p[x].max);
                    p[x].max=u;
                }else build_chain(u);
            }
    }
    void pushdown(int index)
    {
        int l=lef(index),r=rig(index);
        a[l].tag=a[l].lcol=a[l].rcol=a[index].tag;
        a[r].tag=a[r].lcol=a[r].rcol=a[index].tag;
        a[l].num=a[r].num=1;
        a[index].tag=0;
    }
    void tree_modify(int index,int L,int R)
    {
        int l=a[index].l,r=a[index].r,mid=(l+r)>>1;
        if (L<=l && r<=R) {
            a[index].tag=a[index].lcol=a[index].rcol=col;
            a[index].num=1;
            return;
        }
        if (a[index].tag) pushdown(index);
        if (L<=mid) tree_modify(lef(index),L,R);
        if (mid<R) tree_modify(rig(index),L,R);
        a[index].tag=0;
        l=lef(index);r=rig(index);
        a[index].lcol=a[l].lcol;
        a[index].rcol=a[r].rcol;
        a[index].num=a[l].num+a[r].num-(a[l].rcol==a[r].lcol);
    }
    Triple tree_query(int index,int L,int R)
    {
        int l=a[index].l,r=a[index].r,mid=(l+r)>>1,lcol=-1,rcol=-1;
        if (L<=l && r<=R) return Triple(a[index].num,a[index].lcol,a[index].rcol);
        if (a[index].tag) pushdown(index);
        int res=0,count=0;
        Triple lc,rc;
        if (L<=mid) {
            ++count;
            lc=tree_query(lef(index),L,R);
            res+=lc.num;
            lcol=lc.lcol,rcol=lc.rcol;
        }
        if (mid< R) {
            ++count;
            rc=tree_query(rig(index),L,R);
            res+=rc.num;
            if (lcol==-1) lcol=rc.lcol;
            rcol=rc.rcol;
        }
        res-=(count==2 && a[lef(index)].rcol==a[rig(index)].lcol);
        return Triple(res,lcol,rcol);
    }
    void modify(int u,int v)
    {
        while (p[u].head!=p[v].head) {
            if (p[p[u].head].d<p[p[v].head].d) std::swap(u,v);
            tree_modify(p[u].seg_root,1,getindex(u));
            u=p[p[u].head].p;
        }
        if (p[u].d>p[v].d) std::swap(u,v);
        tree_modify(p[u].seg_root,getindex(u),getindex(v));
    }
    int query(int u,int v)
    {
        int res=0,lcolu=-1,lcolv=-1;
        while (p[u].head!=p[v].head) {
            if (p[p[u].head].d<p[p[v].head].d) std::swap(u,v),std::swap(lcolu,lcolv);
            Triple tmp=tree_query(p[u].seg_root,1,getindex(u));
            res+=tmp.num-(tmp.rcol==lcolu);
            //printf("%d ",res);
            lcolu=tmp.lcol;
            u=p[p[u].head].p;
        }
        if (p[u].d>p[v].d) std::swap(u,v),std::swap(lcolu,lcolv);
        Triple tmp=tree_query(p[u].seg_root,getindex(u),getindex(v));
        res+=tmp.num-(tmp.rcol==lcolv)-(tmp.lcol==lcolu);
        //printf("%d\n",res);
        return res;
    }
    inline void addedge(int x,int y)
    {
        es[++ec].next=son[x];
        son[x]=ec;
        es[ec].link=y;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("2243.in","r",stdin);
        freopen("2243.out","w",stdout);
        #endif
        scanf("%d%d",&n,&m);
        int x,y;
        for (int i=1;i<=n;++i) scanf("%d",&p[i].value);
        for (int i=1;i<n;++i) {
            scanf("%d%d",&x,&y);
            addedge(x,y);addedge(y,x);
        }
        tr(1,1);
        build_chain(1);
        scanf("\n");
        while (m--) {
            char c;
            scanf("%c",&c);
            if (c=='C') {
                scanf("%d%d%d\n",&x,&y,&col);
                modify(x,y);
            }else {
                scanf("%d%d\n",&x,&y);
                printf("%d\n",query(x,y));
            }
        }
    }
    

    3.bzoj1146 (区间第k大查询,有点难,需要树套树,注意二分)

    //linenums won't be copied

    /**************************************************************
        Problem: 1146
        User: lazycal
        Language: C++
        Result: Accepted
        Time:15296 ms
        Memory:49280 kb
    ****************************************************************/
                
    #include <cstdio>
    #include <algorithm>
    const int N=80000+9;
    struct node
    {
        int l,r,key,size,p;
        node (const int a=0,const int b=0,const int c=0,
              const int d=0,const int e=0):
            l(a),r(b),key(c),size(d),p(e){}
    }a[2000000];
    struct seg_node
    {
        int l,r,root,lc,rc,head_index;
        seg_node (const int a=0,const int b=0,const int c=0,
                  const int d=0,const int e=0,const int f=0):
            l(a),r(b),root(c),lc(d),rc(e),head_index(f){}
    }sn[N*2];
    struct point
    {
        int d,size,time,seg_root,head,max,p;
    }p[N];
    struct edge
    {
        int link,next;
    }es[N*2];
    int n,q,ec,son[N],nbt,nst,t[N];
    inline int lef(int x){return sn[x].lc;}
    inline int rig(int x){return sn[x].rc;}
    inline int getindex(int x){return p[x].d-p[p[x].head].d+1;}
    inline void addchild(int p,int y,int x)
    {
        if (a[p].l==x) a[p].l=y;
        else a[p].r=y;
    }
    inline void rig_rotate(int x)
    {
        int y=a[x].l;
        a[y].p=a[x].p;
        a[x].p=y;
        a[a[y].r].p=x;
        addchild(a[y].p,y,x);
        a[x].l=a[y].r;
        a[y].r=x;
        a[y].size=a[x].size;
        a[x].size=a[a[x].l].size+a[a[x].r].size+1;
    }
    inline void lef_rotate(int x)
    {
        int y=a[x].r;
        a[y].p=a[x].p;
        a[x].p=y;
        a[a[y].l].p=x;
        addchild(a[y].p,y,x);
        a[x].r=a[y].l;
        a[y].l=x;
        a[y].size=a[x].size;
        a[x].size=a[a[x].l].size+a[a[x].r].size+1;
    }
    inline void splay(int x,int par,int &root)
    {
        while (a[x].p!=par) {
            int y=a[x].p,z=a[y].p;
            if (z!=par && a[y].l==x && a[z].l==y) rig_rotate(z),rig_rotate(y);
            else if (z!=par && a[y].r==x && a[z].r==y) lef_rotate(z),lef_rotate(y);
            else if (a[y].l==x) rig_rotate(y);
            else lef_rotate(y);
        }
        if (!par) root=x;
    }
    void insert(int x,int &root,int Index)
    {
        int index=root,par=0;
        while (index) {
            ++a[index].size;
            if (a[index].key<x) par=index,index=a[index].l;
            else par=index,index=a[index].r;
        }
        a[Index]=node(0,0,x,1,par);
        if (a[par].key<a[Index].key) a[par].l=Index;
        else a[par].r=Index;
        splay(Index,0,root);
    }
    int build_bal_tree(int l,int r)
    {
        int root=++nbt;
        a[nbt]=node(nbt+1,0,-0x7fffffff,2,0);
        ++nbt;
        a[nbt]=node(0,0,0x7fffffff,1,nbt-1);
        for (int i=l;i<=r;++i) 
            insert(p[t[i]].time,root,++nbt);
        return root;
    }
    int build_seg_tree(int l,int r)
    {
        int index=++nst;
        if (l==r) {
            sn[index]=seg_node(l,r,build_bal_tree(l,r),0,0,nbt+3);
            return index;
        }
        int lc=build_seg_tree(l,(l+r)>>1),
            rc=build_seg_tree(((l+r)>>1)+1,r);
        sn[index]=seg_node(l,r,build_bal_tree(l,r),lc,rc,nbt+3);
        return index;
    }
    void build_chain(int x)
    {
        int k=0,root=x;
        while (x) {
            p[x].seg_root=nst+1;
            p[x].head=root;
            t[++k]=x;
            x=p[x].max;
        }
        build_seg_tree(1,k);
    }
    void tr(int x,int d)
    {
        p[x].d=d;p[x].size=1;
        for (int i=son[x];i;i=es[i].next)
            if (es[i].link!=p[x].p) {
                int u=es[i].link;
                p[u].p=x;
                tr(u,d+1);
                p[x].size+=p[u].size;
                if (p[u].size>p[p[x].max].size) {
                    if (p[x].max) build_chain(p[x].max);
                    p[x].max=u;
                }else build_chain(u);
            }
    }
    int bal_rank(int k,int index)
    {
        ++k;
        while (k!=a[a[index].l].size+1) 
            if (k>a[a[index].l].size) k-=a[a[index].l].size+1,index=a[index].r;
            else index=a[index].l;
        return index;
    }
    int bal_query(int x,int index)
    {
        int res=0;
        while (index) 
            if (x<a[index].key) res+=1+a[a[index].l].size,index=a[index].r;
            else index=a[index].l;
        --res;
        return res;
    }
    void remove(int &root,int index)
    {
        splay(index,0,root);
        int pred=a[index].l,succ=a[index].r;
        while (a[pred].r) pred=a[pred].r;
        while (a[succ].l) succ=a[succ].l;
        splay(pred,0,root);splay(succ,root,root);
        --a[root].size;--a[a[root].r].size;
        a[a[root].r].l=0;
    }
    void modify(int x,int y)
    {
        int seg_index=p[x].seg_root,index=getindex(x);
        while (seg_index) {
            int &root=sn[seg_index].root;
            remove(root,index-sn[seg_index].l+sn[seg_index].head_index);
            insert(y,root,index-sn[seg_index].l+sn[seg_index].head_index);
            //print(seg_index);
            int mid=(sn[seg_index].l+sn[seg_index].r)>>1;
            if (index<=mid) seg_index=sn[seg_index].lc;
            else seg_index=sn[seg_index].rc;
        }
        p[x].time=y;
    }
    int seg_query(int index,int L,int R,int x)
    {
        int l=sn[index].l,r=sn[index].r,mid=(l+r)>>1,res=0;
        if (L<=l && r<=R) 
            return bal_query(x,sn[index].root);
        if (L<=mid) res+=seg_query(lef(index),L,R,x);
        if (mid< R) res+=seg_query(rig(index),L,R,x);
        return res;
    }
    int rank(int x,int u,int v)
    {
        int res=0;
        while (p[u].head!=p[v].head) {
            if (p[p[u].head].d<p[p[v].head].d) std::swap(u,v);
            res+=seg_query(p[u].seg_root,1,getindex(u),x);
            u=p[p[u].head].p;
        }
        if (p[u].d>p[v].d) std::swap(u,v);
        res+=seg_query(p[u].seg_root,getindex(u),getindex(v),x);
        return res;
    }
    void query(int k,int u,int v)
    {
        int l=-2,r=100000009;
        while (l+1<r) {
            int mid=(l+r)>>1;
            if (rank(mid,u,v)<=k-1) r=mid;
            else l=mid;
        }
        if (l==-2) puts("invalid request!");
        else printf("%d\n",r);
    }
    inline void addedge(int x,int y)
    {
        es[++ec].next=son[x];
        son[x]=ec;
        es[ec].link=y;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("1146.in","r",stdin);
        freopen("1146.out","w",stdout);
        #endif
        scanf("%d%d",&n,&q);
        for (int i=1;i<=n;++i) scanf("%d",&p[i].time);
        int x,y,k;
        for (int i=1;i<n;++i) {
            scanf("%d%d",&x,&y);
            addedge(x,y);addedge(y,x);
        }
        tr(1,1);
        build_chain(1);
        while (q--) {
            scanf("%d%d%d",&k,&x,&y);
            if (!k) modify(x,y);
            else query(k,x,y);
        }
    }
    
  • 相关阅读:
    python+selenium自动化软件测试(第7章):Page Object模式
    python+selenium自动化软件测试(第6章):selenium phantomjs页面解析使用
    python+selenium自动化软件测试(第5章):Selenium Gird
    python+selenium自动化软件测试(第3章):unittest
    python+selenium自动化软件测试(第2章):WebDriver API
    python+selenium自动化软件测试(第1章):环境搭建,你也可以直接用Anaconda!
    js 躲避球游戏
    ES6基础教程,常见特性随笔
    JS 回到顶端 back to top
    单Js 的重力游戏开发
  • 原文地址:https://www.cnblogs.com/lazycal/p/2862177.html
Copyright © 2011-2022 走看看