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

            近期学习树链剖分,在网上搜了非常多文章,这里推荐一篇博客:点击打开链接

            这篇博客讲的非常细致。在了解了基本原理之后,能够学习一下kuangbin大大的模板:


    定义部分:

    struct Edge
    {
        int to,next;
    }edge[2*maxn];//树的边
    
    int head[maxn],tot;//邻接表
    int top[maxn];//节点所在重链的最高点
    int fa[maxn];//节点的父亲节点
    int deep[maxn];//节点的深度
    int num[maxn];//节点子树的节点数
    int p[maxn];//p与父节点间连线在线段树中的位置
    int fp[maxn];//线段树中fp位置相应的的节点
    int tson[maxn];//重儿子
    int pos;//线段树中的位置


    初始化部分:

    void init()   //初始化操作
    {
        tot=1;
        memset(head,-1,sizeof(head));
        pos=1;
        memset(tson,-1,sizeof(tson));
    }


    邻接表的加边操作:

    void addedge(int u,int v)     //邻接表的加边操作
    {
        edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
    }


    以下就是两个重点操作:1.求fa,deep,son,num   2.求top,p

    void  dfs(int u,int pre,int d) //求fa,deep,son,num
    {
        fa[u]=pre;
        deep[u]=d;
        num[u]=1;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=pre)
            {
                dfs(v,u,d+1);
                num[u]+=num[v];
                if(tson[u]==-1||num[tson[u]]<num[v])
                tson[u]=v;
            }
        }
    }
    
    void getpos(int u,int sp)//求top,p
    {
        top[u]=sp;
        p[u]=pos++;
        fp[p[u]]=u;
        if(tson[u]==-1) return;
        getpos(tson[u],sp);
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=tson[u]&&v!=fa[u])
              getpos(v,v);
        }
    }


    以下这个是依据题目而订的操作,就是訪问u->v链,并运行操作:

    void Change(int u,int v,int value)  //区间更新
    {
        int f1=top[u],f2=top[v];
        while(f1!=f2)
        {
            if(deep[f1]<deep[f2])
            {
                swap(f1,f2);
                swap(u,v);
            }
            //详细操作
            u=fa[f1];f1=top[u];
        }
        if(deep[v]<deep[u])
        {
            swap(u,v);
        }
          //详细操作
    }

    注意:树链剖分能够分为两种,一种是边权处理,一种是点权处理。点权处理没什么好说的,边权处理时,我们用儿子节点代表该节点与父节点之间的边的权值。


    hdu 3966 Aragorn's Story(点权)

    这道题使用的是树链剖分+树状数组,注意查询t节点时,查询的是p[t](常常忘记,WA了好几次 = =。)


    #pragma comment(linker, "/STACK:1024000000,1024000000")
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    #define maxn 50010
    using namespace std;
    typedef long long  ll;
    
    struct Edge
    {
        int to,next;
    }edge[2*maxn];//树的边
    
    int head[maxn],tot;//邻接表
    int top[maxn];//节点所在重链的最高点
    int fa[maxn];//节点的父亲节点
    int deep[maxn];//节点的深度
    int num[maxn];//节点子树的节点数
    int p[maxn];//p与父节点间连线在线段树中的位置
    int fp[maxn];//线段树中fp位置相应的的节点
    int tson[maxn];//重儿子
    int pos;//线段树中的位置
    
    
    void init()   //初始化操作
    {
        tot=1;
        memset(head,-1,sizeof(head));
        pos=1;
        memset(tson,-1,sizeof(tson));
    }
    
    void addedge(int u,int v)     //邻接表的加边操作
    {
        edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
    }
    
    void  dfs(int u,int pre,int d) //求fa,deep,son,num
    {
        fa[u]=pre;
        deep[u]=d;
        num[u]=1;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=pre)
            {
                dfs(v,u,d+1);
                num[u]+=num[v];
                if(tson[u]==-1||num[tson[u]]<num[v])
                tson[u]=v;
            }
        }
    }
    
    void getpos(int u,int sp)//求top,p
    {
        top[u]=sp;
        p[u]=pos++;
        fp[p[u]]=u;
        if(tson[u]==-1) return;
        getpos(tson[u],sp);
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=tson[u]&&v!=fa[u])
              getpos(v,v);
        }
    }
    
    int son[maxn];
    int n,m,k;
    
    int lowbit(int x)
    {
        return x&(-x);
    }
    
    void add(int d,int value)
    {
        while(d<=n)
        {
            son[d]+=value;
            d+=lowbit(d);
        }
    }
    
    int Query(int d)
    {
        int sum=0;
        while(d>0)
        {
            sum+=son[d];
            d-=lowbit(d);
        }
        return sum;
    }
    
    
    void Change(int u,int v,int value)  //区间更新
    {
        int f1=top[u],f2=top[v];
        while(f1!=f2)
        {
            if(deep[f1]<deep[f2])
            {
                swap(f1,f2);
                swap(u,v);
            }
            add(p[f1],value);
            add(p[u]+1,-value);
            u=fa[f1];f1=top[u];
        }
        if(deep[v]<deep[u])
        {
            swap(u,v);
        }
        add(p[u],value);
        add(p[v]+1,-value);
    }
    int v[maxn];
    int main()
    {
        char s[10];
        int l,r,t;
        while(scanf("%d%d%d",&n,&m,&k)!=EOF)
        {
            init();
            memset(son,0,sizeof(son));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&v[i]);
            }
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&l,&r);
                addedge(l,r);
                addedge(r,l);
            }
            dfs(1,0,0);
            getpos(1,1);
            for(int i=1;i<=n;i++)
            {
                add(p[i],v[i]);
                add(p[i]+1,-v[i]);
            }
            while(k--)
            {
                scanf("%s",s);
                if(s[0]=='Q')
                {
                    scanf("%d",&t);
                    printf("%d
    ",Query(p[t]));   //就是这里,不是t
                }
                else
                {
                    scanf("%d%d%d",&l,&r,&t);
                    if(s[0]=='D')t=-t;
                    Change(l,r,t);
                }
    
            }
        }
        return 0;
    }
    


    SPOJ QTREE(边权) 树链剖分+线段树


    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    #define maxn 10010
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    using namespace std;
    typedef long long  ll;
    
    struct Edge
    {
        int to,next;
    }edge[2*maxn];//树的边
    
    int head[maxn],tot;//邻接表
    int top[maxn];//节点所在重链的最高点
    int fa[maxn];//节点的父亲节点
    int deep[maxn];//节点的深度
    int num[maxn];//节点子树的节点数
    int p[maxn];//p与父节点间连线在线段树中的位置
    int fp[maxn];//线段树中fp位置相应的的节点
    int tson[maxn];//重儿子
    int pos;//线段树中的位置
    
    
    void init()   //初始化操作
    {
        tot=1;
        memset(head,-1,sizeof(head));
        pos=1;
        memset(tson,-1,sizeof(tson));
    }
    
    void addedge(int u,int v)     //邻接表的加边操作
    {
        edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
    }
    
    void  dfs(int u,int pre,int d) //求fa,deep,son,num
    {
        fa[u]=pre;
        deep[u]=d;
        num[u]=1;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=pre)
            {
                dfs(v,u,d+1);
                num[u]+=num[v];
                if(tson[u]==-1||num[tson[u]]<num[v])
                tson[u]=v;
            }
        }
    }
    
    void getpos(int u,int sp)//求top,p
    {
        top[u]=sp;
        p[u]=pos++;
        fp[p[u]]=u;
        if(tson[u]==-1) return;
        getpos(tson[u],sp);
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=tson[u]&&v!=fa[u])
              getpos(v,v);
        }
    }
    
    
    //以下是线段树模板了,不介绍了
    struct segment
    {
        int l,r;
        int value;
        int nv;
    } son[maxn<<2];
    
    
    void PushUp(int rt)
    {
        son[rt].value=max(son[rt<<1].value,son[rt<<1|1].value);
    }
    
    
    void PushDown(int rt)
    {
        if(son[rt].nv)
        {
            son[rt<<1].nv=son[rt].nv;
            son[rt<<1|1].nv=son[rt].nv;
            son[rt<<1].value=son[rt<<1].nv;
            son[rt<<1|1].value=son[rt<<1|1].nv;
            son[rt].nv=0;
        }
    }
    
    
    void Build(int l,int r,int rt)
    {
        son[rt].l=l;
        son[rt].r=r;
        if(l==r)
        {
            son[rt].value=1;
            return;
        }
        int m=(l+r)/2;
        Build(lson);
        Build(rson);
        PushUp(rt);
    }
    
    //线段树单点更新
    void Update_1(int p,int value,int rt)
    {
        if(son[rt].l==son[rt].r)
        {
            son[rt].value=value;
            return;
        }
    
        //PushDown(rt);
    
        int m=(son[rt].l+son[rt].r)/2;
        if(p<=m)
            Update_1(p,value,rt<<1);
        else
            Update_1(p,value,rt<<1|1);
    
        PushUp(rt);
    }
    
    //线段树区间更新
    void Update_n(int w,int l,int r,int rt)
    {
        if(son[rt].l==l&&son[rt].r==r)
        {
            son[rt].value+=w*(r-l+1);
            son[rt].nv+=w;
            return;
        }
    
        PushDown(rt);
    
        int m=(son[rt].l+son[rt].r)/2;
    
        if(r<=m)
            Update_n(w,l,r,rt<<1);
        else if(l>m)
            Update_n(w,l,r,rt<<1|1);
        else
        {
            Update_n(w,lson);
            Update_n(w,rson);
        }
        PushUp(rt);
    }
    
    
    int  Query(int l,int r,int rt)
    {
        if(son[rt].l==l&&son[rt].r==r)
        {
            return son[rt].value;
        }
    
        //PushDown(rt);
    
        int ret=0;
        int m=(son[rt].l+son[rt].r)/2;
    
        if(r<=m)
            ret=Query(l,r,rt<<1);
        else if(l>m)
            ret=Query(l,r,rt<<1|1);
        else
        {
            ret=Query(lson);
            ret=max(Query(rson),ret);
        }
        return ret;
    }
    
    int find(int u,int v)  //寻找u->v的链上的最值
    {                            //主要思想就是比較u和v的是否在同一条重链上
        int f1=top[u],f2=top[v];//若在同一条重链上(f1=f2),直接求就能够了
        int tmp=0;              //由于同一条重链上的点在线段树中是连续的
        while(f1!=f2)           //若不在同一条重链上。那么我们比較两个点的深度
        {                       //一直向上寻找,直到连个点在同一条重链上
            if(deep[f1]<deep[f2])
            {
                swap(f1,f2);
                swap(u,v);
            }
            tmp=max(tmp,Query(p[f1],p[u],1));
            u=fa[f1];f1=top[u];
        }
        if(u==v) return tmp;
        if(deep[u]<deep[v])
        {
            swap(u,v);
        }
        return max(tmp,Query(p[tson[v]],p[u],1));
    }
    
    int e[maxn][3];
    int main()
    {
        char s[10];
        int cas,l,r;
        scanf("%d",&cas);
        while(cas--)
        {
            int n;
            scanf("%d",&n);
            init();
            for(int i=1;i<n;i++)
            {
                scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
                addedge(e[i][0],e[i][1]);
                addedge(e[i][1],e[i][0]);
            }
            dfs(1,1,0);
            getpos(1,1);
            Build(1,pos-1,1);
            for(int i=1;i<n;i++)
            {
                if(deep[e[i][0]]<deep[e[i][1]])
                    swap(e[i][0],e[i][1]);
                Update_1(p[e[i][0]],e[i][2],1);
            }
            do
            {
                scanf("%s",s);
                if(s[0]=='D') break;
                scanf("%d%d",&l,&r);
                if(s[0]=='Q') printf("%d
    ",find(l,r));
                else Update_1(p[e[l][0]],r,1);
            }while(1);
        }
        return 0;
    }
    

     








  • 相关阅读:
    可视化XHTML编辑器
    诺基亚E63内存清理法
    C语言第0次作业
    C语言博客作业03函数
    C博客作业01分支、顺序结构
    C语言博客作业02循环结构
    心情随笔
    沉没成本
    检索了MEG 和EEG,以及棘波的论文
    解决投稿问题
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5199081.html
Copyright © 2011-2022 走看看