zoukankan      html  css  js  c++  java
  • Query on a tree——树链剖分整理

    树链剖分整理

    树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护。

    通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点vsize[v]最大的vu的边是重边,其它边是轻边,其中size[v]是以v为根的子树的节点个数,全部由重边组成的路径是重路径,根据论文上的证明,任意一点到根的路径上存在不超过logn条轻边和logn条重路径。

    这样我们考虑用数据结构来维护重路径上的查询,轻边直接查询。

    通常用来维护的数据结构是线段树,splay较少见。

     

    具体步骤

    预处理

    第一遍dfs

        求出树每个结点的深度dep[x],其为根的子树大小siz[x]

    ,其重儿子,以及祖先的信息fa[x]表示x的直接父亲,

    第二遍dfs

    根节点为起点,向下拓展构建重链

    选择最大的一个子树的根继承当前重链

    其余节点,都以该节点为起点向下重新拉一条重链

    搞出top[x]top[x]表示x所在链的端点

    给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。

    搞出pos[x],pos[x]表示在线段树中以x为下端点的标号(一般不维护边)

    把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可

    修改操作

    1、单独修改一个点的权值    //例题中没用

    根据其编号直接在数据结构中修改就行了。

    2、修改点u和点v的路径上的权值

    1)若uv在同一条重链上

    直接用数据结构修改pos[u]pos[v]间的值。

    2)若uv不在同一条重链上

    一边进行修改,一边将uv往同一条重链上靠,然后就变成了情况(1)。

    查询操作

        查询操作的分析过程同修改操作

    题目不同,选用不同的数据结构来维护值,通常有线段树和splay

      

    例题、SPOJ 305:Query on a tree
    题意:10000个点的树,有边权(<=1000000),支持两个操作:
    1、CHANGE i ti 把第i条变的权改为ti
    2、QUERY a b 查询a,b两点间路径上的最大边
    20组数据
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define lc k<<1
    #define rc k<<1|1
    #define IN inline
    #define R register
    using namespace std;
    const int N=1e4+10;
    IN int read(){
        R int x=0;R bool f=1;
        R char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=0;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    struct node{
        int u,v,w,next;
    }e[N<<1];
    int n,T,tot,num,head[N],fa[N],top[N],pos[N],dep[N],siz[N],son[N];
    int a[N<<2];
    void add(int x,int y,int z){
        e[++tot].u=x;
        e[tot].v=y;
        e[tot].w=z;
        e[tot].next=head[x];
        head[x]=tot;
    }
    void dfs(int u,int f,int de){
        fa[u]=f;dep[u]=de;siz[u]=1;
        for(int i=head[u],v;i;i=e[i].next){
            v=e[i].v;
            if(v!=f){
                dfs(v,u,de+1);
                siz[u]+=siz[v];
                if(!son[u]||siz[son[u]]<siz[v]){
                    son[u]=v;
                }
            }
        }
    }
    void getpos(int u,int tp){
        top[u]=tp;
        pos[u]=++num;
        if(!son[u]) return ;
        getpos(son[u],tp);
        for(int i=head[u],v;i;i=e[i].next){
            v=e[i].v;
            if(v!=son[u]&&v!=fa[u]){
                getpos(v,v);
            }
        }
    }
    void change(int k,int l,int r,int pos,int val){
        if(l==r){
            a[k]=val;
            return;
        }
        int mid=l+r>>1;
        if(pos<=mid) change(lc,l,mid,pos,val);
        else change(rc,mid+1,r,pos,val);
        a[k]=max(a[lc],a[rc]);
    }
    int query(int k,int l,int r,int x,int y){
        if(l==x&&y==r) return a[k];
        int mid=l+r>>1;
        if(y<=mid) return query(lc,l,mid,x,y);
        else if(x>mid) return query(rc,mid+1,r,x,y);
        else return max(query(lc,l,mid,x,mid),query(rc,mid+1,r,mid+1,y));
    }
    int find(int u,int v){
        int tp1=top[u],tp2=top[v],ans=0;
        while(tp1!=tp2){
            if(dep[tp1]<dep[tp2]){
                swap(tp1,tp2);
                swap(u,v);
            }
            ans=max(ans,query(1,1,num,pos[tp1],pos[u]));
            u=fa[tp1];tp1=top[u];
        }
        if(u==v) return ans;
        if(dep[u]>dep[v]) swap(u,v);
        return max(ans,query(1,1,num,pos[u]+1,pos[v]));
    }
    void Cl(){
        tot=0;num=0;
        memset(a,0,sizeof a);
        memset(fa,0,sizeof fa);
        memset(head,0,sizeof head);
        memset(pos,0,sizeof pos);
        memset(top,0,sizeof top);
        memset(son,0,sizeof son);
        memset(dep,0,sizeof dep);
    }
    int main(){
        for(T=read();T--;){
            Cl();
            n=read();
            for(int i=1,x,y,z;i<n;i++){
                x=read();y=read();z=read();
                add(x,y,z);add(y,x,z);
            }
            dfs(1,0,1);
            getpos(1,1);
            for(int i=1,t=(n-1)*2;i<t;i+=2){
                if(dep[e[i].v]<dep[e[i].u]) swap(e[i].u,e[i].v);
                change(1,1,num,pos[e[i].v],e[i].w);
            }
            char ch[20];
            for(int x,y;;){
                scanf("%s",ch);
                if(ch[0]=='D') break;
                if(ch[0]=='C'){
                    x=read();y=read();
                    change(1,1,num,pos[e[x*2-1].v],y);
                }
                else{
                    x=read();y=read();
                    printf("%d
    ",find(x,y));
                }
            }
        }
        return 0;
    }

     

  • 相关阅读:
    [程序员必备工具]分享一款不错的个人代码个人知识管理软件wiz
    移动开发,如何选择手机软件开发​平台?
    Best Wishes,my Married Friend!
    三年来最开心的一天!
    “世事无绝对”也是相对的……
    又到世界CUP……
    利物浦,总是在我情绪低落时给我鼓舞!
    一种感觉
    How to break to loop? It frustrates me!
    取舍有度,学会放弃……
  • 原文地址:https://www.cnblogs.com/shenben/p/6194491.html
Copyright © 2011-2022 走看看