• 树链剖分入门-Hdu3966 Aragorn's Story


    AC通道:http://acm.hdu.edu.cn/showproblem.php?pid=3966

    [题目大意]

      一棵树上每个点有权值,每次支持三种操作:给[a,b]路径上的所有节点的权值加上k,给[a,b]路径上的所有节点的权值减去k,以及询问a的权值.

    [分析]

      这是一道树链剖分模板题.

      树链剖分,就是将树化成了许多链,将这些链用数据结构保存起来,再去维护这个数据结构.

      假设给的树就是一条链,这道题当然很好办:直接将链用线段树存了,因为[a,b]的路径在线段树上也是连续的一段,那么修改一段就是log(n)的,询问节点也可以log(n)完成.

      但是给的是一棵树,树上两点间的路径虽然也是唯一确定的,但是在线段树上就不一定是连续的一段了.  

      于是乎,就需要一种将每条树上路径分成若干个连续段的算法.

      这就是树链剖分了.

      

      首先定义一下重儿子的概念:v是u的重儿子,当且仅当size(v)>=size(v')对于任意u的儿子v'成立.就是u的儿子中所在子树最大的儿子.

      树链剖分将树分成重链和轻边,重链指的是每个节点与其重儿子相连,其重儿子再与其重孙子相连得到的链.轻边就是连接重链的边.

      [我这儿没图....要图找别人:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html 这个图其实也长得不怎么好看]

      通过这样一个划分,就有了优美的性质:任意两点间的路径上最多有log(n)条重链.

      然后就可以将重链上的点连续地存进线段树中,每次将路径划分成若干个重链的部分来处理就可以了.

      这样下来每条路径处理就是log(n)*log(n)的.

      具体步骤:

        首先一遍dfs.处理出一些东西:每个节点子树的大小->每个点的重儿子,每个点的深度[这个在划分路径的时候有用],每个节点的父节点.

        然后再一遍dfs.给每个点标号,注意将重链的编号连续,同时记录每个点所在重链的顶端节点[这个怎么操作可以具体看代码]

        然后建线段树:跟往常一样的建立方法.

        

        询问:在线段树上询问,记得下传标记就好.

        修改:思考怎么划分,如果询问的两点,假设是x,y.

        当x,y处于同一条重链时:直接用线段树的区间修改,当不是同一条重链时,就需要将他们移动到同一条重链.

        首先判断x,y所在重链,谁的上端距离根更远,假设是x,那么将x调至其所在重链的上端的父亲节点[这样就移动到了另一条重链上],同时还要将x到重链上端的这一部分在线段树中加tag.

        依次这样操作,就可以等到x,y在同一条重链上的时候了.[具体也可以看代码]

      希望通过这道题,大家也就能大概理解树链剖分的思想所在了...

      献上代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    inline int in(){
        int x=0,f=1;char ch=getchar();
        while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
        if(ch=='-') f=-1,ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    
    const int maxn=50010;
    
    int n,m,q;
    int cnt;
    int a[maxn],id[maxn],Idex,rk[maxn];
    int depth[maxn],sz[maxn],fa[maxn];
    int top[maxn],son[maxn],head[maxn];
    char ord[2];
    
    struct Spot{
        int data,next;
    }spot[maxn<<1];
    
    #define now spot[pt].data
    #define then spot[pt].next
    
    struct Node{
        int dt,tg;
    }s[maxn<<2];
    
    inline void add(int u,int v){
        spot[cnt].data=v;spot[cnt].next=head[u];head[u]=cnt++;
        spot[cnt].data=u;spot[cnt].next=head[v];head[v]=cnt++;
    }
    
    void dfs1(int x,int f){
        sz[x]=1,fa[x]=f;
        for(int pt=head[x];pt!=-1;pt=then)
            if(now!=f){
                depth[now]=depth[x]+1;
                dfs1(now,x);
                sz[x]+=sz[now];
                if(son[x]<0 || sz[now]>sz[son[x]])
                    son[x]=now;
            }
    }
    
    void dfs2(int x,int tp){
        top[x]=tp,id[x]=++Idex,rk[id[x]]=x;
        if(son[x]<0) return;
        dfs2(son[x],tp);
        for(int pt=head[x];pt!=-1;pt=then)
            if(now!=fa[x] && now!=son[x])
                dfs2(now,now);
    }
    
    void build(int l,int r,int rt){
        s[rt].dt=s[rt].tg=0;
        if(l==r){
            s[rt].dt=a[rk[l]];return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
    }
    
    inline void push_down(int x){
        if(s[x].tg){
            s[x<<1].tg+=s[x].tg,s[x<<1].dt+=s[x].tg;
            s[x<<1|1].tg+=s[x].tg,s[x<<1|1].dt+=s[x].tg;
            s[x].tg=0;
        }
    }
    
    void add_tag(int l,int r,int x,int y,int rt,int val){
        if(l==x && r==y){
            s[rt].dt+=val,s[rt].tg+=val;
            return;
        }
        int mid=(l+r)>>1;
        if(y<=mid) add_tag(l,mid,x,y,rt<<1,val);
        else if(x>mid) add_tag(mid+1,r,x,y,rt<<1|1,val);
        else add_tag(l,mid,x,mid,rt<<1,val),add_tag(mid+1,r,mid+1,y,rt<<1|1,val);
    }
    
    int query(int l,int r,int rt,int k){
        if(l==r)
            return s[rt].dt;
        push_down(rt);
        int mid=(l+r)>>1;
        if(k<=mid) return query(l,mid,rt<<1,k);
        else return query(mid+1,r,rt<<1|1,k);
    }
    
    void modify(int x,int y,int val){
        while(top[x]!=top[y]){
            if(depth[top[x]]<depth[top[y]]) swap(x,y);
            add_tag(1,n,id[top[x]],id[x],1,val);
            x=fa[top[x]];
        }
        if(id[x]>id[y]) swap(x,y);
        add_tag(1,n,id[x],id[y],1,val);
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("3966.in","r",stdin);
        freopen("3966.out","w",stdout);
    #endif
        
        int u,v,x;
        
        while(~scanf("%d%d%d",&n,&m,&q)){
            Idex=cnt=0;
            for(int i=1;i<=n;i++)
                a[i]=in(),head[i]=son[i]=-1;
            for(int i=1;i<n;i++)
                u=in(),v=in(),add(u,v); 
        
            dfs1(1,0);
            dfs2(1,1);
            build(1,n,1);
        
            while(q--){
                scanf("%s",ord);
                if(ord[0]=='Q'){
                    x=in();
                    printf("%d
    ",query(1,n,1,id[x]));
                }
                else{
                    u=in(),v=in(),x=in();
                    if(ord[0]=='D') x=-x;
                    modify(u,v,x);
                }
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    go语言xrom使用
    go语言算法
    go语言递归
    go语言map(字典)
    GO语言实现小技巧
    偶遇递归树
    Python中字典对象根据字典某一个key和values去重
    python中将字符串格式转字典
    Azure媒体服务的Apple FairPlay流功能正式上线
    SVG裁剪和平移的顺序
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/5118254.html
Copyright © 2020-2023  润新知