zoukankan      html  css  js  c++  java
  • 浅谈树链剖分(C++、算法、树结构)

    关于数链剖分我在网上看到的有几个比较好的讲解,本篇主要是对AC代码的注释(感谢各位witer的提供)

    这是讲解

    http://www.cnblogs.com/kuangbin/archive/2013/08/15/3259083.html

    另一个是百度文库

    http://wenku.baidu.com/link?url=DY8CAbwdjitIiv8XQsHmVPi--dQAqw5z6dc_6N1Plh4u5Nfc1aCADQm4oAvt4Sqe1mXSixezzK4lRxofQKMX9cNzJwYwQhpGJZuMSTI3Ktq

    这是AC代码(以bzoj1036为例题)

    http://www.cnblogs.com/kuangbin/archive/2013/08/15/3259083.html

    但是我相信很多人有和我一样的经历,就是看各种大神的代码的时候只是膜拜他们的长度和复杂,但是大神们却并不对代码进行讲解,导致我们和多人看不懂。下来还得自己理解。

    这样会花费很长的时间。。。。TAT!

    下面是我对AC代码的注释,原文并没有那么多。其中没有关于线段树的知识,只是对剖分时进行了解释。对于不了解线段树的读者请自行百度。。。。。

    希望能对读者有较大的帮助。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<string>
    using namespace std;
    const int MAXN = 30010;
    struct Edge//E[i].to为边E[i]指向的点.
    {// E[i].next为边E的上一条边(即E与E的上一条边由同一节点发出).
        int to,next;
    }edge[MAXN*2];
    int head[MAXN],tot;//head[j]为节点j的最后一条发出边.
    int top[MAXN]; //top[v] 表示v所在的重链的顶端节点
    int fa[MAXN]; //父亲节点
    int deep[MAXN];//深树的度
    int num[MAXN]; //num[v]表示以v为根的子树的节点数
    int p[MAXN]; //p[v]表示v在线段树中的位置
    int fp[MAXN];//和p数组相反
    int son[MAXN];//重儿子
    int pos;
    void init()
    {
        tot=0;pos=0;
        memset(head,0,sizeof(head));
        memset(son,-1,sizeof(son));
    }
    void addedge(int u,int v)
    {
        edge[++tot].to=v;edge[tot].next=head[u];head[u]=tot;
    }
    void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
    {
        deep[u]=d;//u 深搜到的点 pre u的前驱 d 深度 
        fa[u]=pre;//完成父亲数组 
        num[u]=1;//u 包含的点数 
        for(int i=head[u];i;i=edge[i].next){
          int v=edge[i].to;//边i的指向点 
          if(v!=pre){//判断是否是i的父亲,若是跳出不是继续深搜
            dfs1(v,u,d+1);//v 深搜到的点 u v的父亲(前驱) 深度+1 
            num[u]+=num[v];//父亲包含的点数=儿子包含的点数+1 
            if(son[u]==-1||num[v]>num[son[u]])//son==-1时,即没有标记重儿子,直接标记 
              son[u]=v;// num[v]>num[son[u]]如果v包含的节点数大于其父亲的重儿子的节点数,v变为 
          }//                                                              其父亲的重儿子 
        }
    }
    void getpos(int u,int sp)//将重儿子连成重链 
    {
        top[u]=sp;//sp为传递变量,即若sp不变,DFS过程中的重儿子均属于以sp为根的重链 
        p[u]=pos++;//将重链的节点放在数组p中,来构建线段树 
        fp[p[u]]=u;//反之 
        if(son[u]==-1) return;//若son为-1,则该点为叶子节点,返回 
        getpos(son[u],sp);//DFS对u的重儿子,将其加入重链中,sp不变 
        for(int i=head[u];i;i=edge[i].next){//边的循环(将一条重链添加完后再对轻边进行添加) 
          int v=edge[i].to;//v是边i的指向点 
          if(v!=son[u]&&v!=fa[u]) getpos(v,v);//(v!=fa[u])处理v是u的父亲的情况 
        }//若v是u的儿子但不是重儿子(即其属于轻边),以自己为根(修改sp)继续DFS形成新的重链  
    }
    struct Node//线段树的点 
    {
        int l,r;
        int sum;
        int Max;
    }segTree[MAXN*3];
    void push_up(int i)
    {
        segTree[i].sum=segTree[i<<1].sum+segTree[(i<<1)|1].sum;
        segTree[i].Max=max(segTree[i<<1].Max,segTree[(i<<1)|1].Max);
    } 
    int s[MAXN];
    void build(int i,int l,int r)//建线段树(不懂的看线段树的讲解)
    {
        segTree[i].l=l;
        segTree[i].r=r;
        if(l==r){
          segTree[i].sum=segTree[i].Max=s[fp[l]];
          return;
        }
        int mid=(l+r)/2;
        build(i<<1,l,mid);
        build((i<<1)|1,mid+1,r);
        push_up(i);//用来储存树上的和、最大值 
    }
    void update(int i,int k,int val)//更新线段树的第k个值为val
    {
        if(segTree[i].l==k&&segTree[i].r==k){
          segTree[i].sum=segTree[i].Max=val;
          return;
        }
        int mid=(segTree[i].l+segTree[i].r)/2;
        if(k<=mid)update(i<<1,k,val);
        else update((i<<1)|1,k,val);
        push_up(i);
    }
    int queryMax(int i,int l,int r)//查询线段树[l,r]区间的最大值
    {
        if(segTree[i].l==l&&segTree[i].r==r){
          return segTree[i].Max;
        }
        int mid=(segTree[i].l+segTree[i].r)/2;
        if(r<=mid) return queryMax(i<<1,l,r);
        else if(l > mid)return queryMax((i<<1)|1,l,r);
        else return max(queryMax(i<<1,l,mid),queryMax((i<<1)|1,mid+1,r));
    }
    int querySum(int i,int l,int r) //查询线段树[l,r]区间的和
    {
        if(segTree[i].l==l&&segTree[i].r==r)
          return segTree[i].sum;
        int mid=(segTree[i].l+segTree[i].r)/2;
        if(r<=mid)return querySum(i<<1,l,r);
          else if(l>mid)return querySum((i<<1)|1,l,r);
            else return querySum(i<<1,l,mid)+querySum((i<<1)|1,mid+1,r);
    }
    int findMax(int u,int v)//查询u->v路径上节点的最大权值
    {
        int f1=top[u],f2=top[v];
        int tmp=-1000000000;
        while(f1!=f2){
          if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
          }
          tmp=max(tmp,queryMax(1,p[f1],p[u]));
          u=fa[f1];
          f1=top[u];
        }
        if(deep[u]>deep[v]) swap(u,v);
        return max(tmp,queryMax(1,p[u],p[v]));
    }
    int findSum(int u,int v) //查询u->v路径上节点的权值的和
    {
        int f1=top[u],f2=top[v];//记录所属链的根节点 
        int tmp=0;
        while(f1!=f2){
          if(deep[f1]<deep[f2]){//同时向上跳 
            swap(f1,f2);
            swap(u,v);
          }
          tmp+=querySum(1,p[f1],p[u]);
          u=fa[f1];
          f1=top[u];
        }
        if(deep[u]>deep[v]) swap(u,v);
        return tmp+querySum(1,p[u],p[v]);
    }
    int main() 
    {
    //    freopen("in.in","r",stdin);
    //    freopen("out.out","w",stdout);
        int n;
        int q;
        char op[20];
        int u,v;
        while(scanf("%d",&n)==1){
          init();
          for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
          }
          for(int i=1;i<=n;i++)
            scanf("%d",&s[i]);
          dfs1(1,0,0);
          getpos(1,1);
          build(1,0,pos-1);
          scanf("%d",&q);
          while(q--){
            scanf("%s%d%d",op,&u,&v);
            if(op[0]=='C')  update(1,p[u],v);//修改单点的值
             else if(strcmp(op,"QMAX") == 0)
                printf("%d
    ",findMax(u,v));//查询u->v路径上点权的最大值
             else printf("%d
    ",findSum(u,v));//查询路径上点权的和
          }
        }
        return 0;
    }

    程序员不是打字员而是作家!

  • 相关阅读:
    【Unity】第10章 Mecanim动画系统
    【Unity】9.3 粒子系统生成器详解
    只用一招,让你Maven依赖下载速度快如闪电
    原来实现项目多环境打包部署是如此的简单
    手写Maven的archetype项目脚手架
    通俗易懂详解Java代理及代码实战
    谈谈分布式版本管理工具Git
    拥抱了IDEA却发现再也回不去Eclipse...
    解决Redisson出现Failed to instantiate [org.redisson.api.RedissonClient]: Factory method 'create' threw exception; nested exception is java.lang.ArrayIndexOutOfBoundsException: 0的问题
    解决org.apache.shiro.session.UnknownSessionException: There is no session with id的问题
  • 原文地址:https://www.cnblogs.com/xtx1999/p/4616345.html
Copyright © 2011-2022 走看看