zoukankan      html  css  js  c++  java
  • 关于树链剖分的一些算法

    参考和学习自:dalao's blog

    树链剖分,一个神奇的算法,用来解决树上的求和,找最值,修改以及许多玄学操作的算法

    其实,上面讲的求和,找最值和修改都是像线段树一样对于序列上的操作。当然如果你用主席树,Splay等高级数据结构该可以做更多的事情

    而树剖就是这样,它能使得那些维护序列的操作和数据结构可以被用在树上

    其实也是一个预处理一样的东西了

    首先先明确一些概念:(不懂也没事)

    • 重结点:子树结点数目最多的结点;

    • 轻节点:父亲节点中除了重结点以外的结点;

    • 重边:父亲结点和重结点连成的边;

    • 轻边:父亲节点和轻节点连成的边;

    • 重链:由多条重边连接而成的路径;

    • 轻链:由多条轻边连接而成的路径;

    然后我们就可以通过一种方法把树上的节点重新编号,使得可以向维护序列一样的维护它们

    我们确定一种标号方式,优先对重链上的点进行编号,然后再轻链

    这样,我们发现:一棵子树中,所有的点都被连续编号了,且重链上的点的标号较小

    因此我们在查询时若两个点在同一条重链上就可以直接查询(编号是连续的)

    如果不是的话就把一个点(深度较深)先移到当前这条重链的顶端,然后向上通过一条轻边即可

    具体看代码吧,毕竟有些东西只可意会,不可言传

    这里给出一些数组的意义

    • dep[]所有节点的深度

    • father[]所有节点的直系father

    • size[]所有以当前节点为根的子树节点个数(包括自己)

    • id[]每个节点树剖后的编号

    • s[]每个节点树剖后新的值,与id相对应

    • son[]每个节点的重儿子

    • top[]每个节点所在的重链的链的顶端

    线段树就不介绍了吧

    CODE

    #include<cstdio>
    #include<cstring>
    const int N=100005;
    typedef long long LL;
    using namespace std;
    struct segtree
    {
        LL add,sum;
    }tree[N<<2];
    struct edge
    {
        int to,next;
    }e[N<<1];
    int n,q,root,p,a[N],head[N],cnt,dep[N],father[N],size[N],id[N],s[N],son[N],top[N],opt,x,y,z,tot;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch=tc();
        while (ch<'0'||ch>'9') ch=tc();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline void write(LL x)
    {
        if (x/10) write(x/10);
        putchar(x%10+'0');
    }
    inline void add(int x,int y)
    {
        e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    }
    inline void swap(int &a,int &b)
    {
        int t=a; a=b; b=t;
    }
    inline void up(int root)
    {
        tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p;
    }
    inline void down(int root,int l,int r)
    {
        if (tree[root].add)
        {
            tree[root<<1].add=(tree[root<<1].add+tree[root].add)%p;
            tree[root<<1|1].add=(tree[root<<1|1].add+tree[root].add)%p;
            tree[root<<1].sum=(tree[root<<1].sum+tree[root].add*l)%p;
            tree[root<<1|1].sum=(tree[root<<1|1].sum+tree[root].add*r)%p;
            tree[root].add=0;
        }
    }
    inline void build(int root,int l,int r)
    {
        if (l==r)
        {
            tree[root].sum=s[l];
            return;
        }
        int mid=l+r>>1;
        build(root<<1,l,mid); build(root<<1|1,mid+1,r);
        up(root);
    }
    inline void modify(int root,int l,int r,int beg,int end,int k)
    {
        if (l>=beg&&r<=end)
        {
            tree[root].add=(tree[root].add+k)%p;
            tree[root].sum=(tree[root].sum+k*(r-l+1))%p;
            return;
        }
        int mid=l+r>>1;
        down(root,mid-l+1,r-mid);
        if (beg<=mid) modify(root<<1,l,mid,beg,end,k);
        if (end>mid) modify(root<<1|1,mid+1,r,beg,end,k);
        up(root);
    }
    inline LL query(int root,int l,int r,int beg,int end)
    {
        if (l>=beg&&r<=end) return tree[root].sum;
        int mid=l+r>>1;
        LL res=0;
        down(root,mid-l+1,r-mid);
        if (beg<=mid) res=(res+query(root<<1,l,mid,beg,end))%p;
        if (end>mid) res=(res+query(root<<1|1,mid+1,r,beg,end))%p;
        return res;
    }
    inline void DFS1(int now,int fa,int d)
    {
        father[now]=fa; dep[now]=d;
        size[now]=1; int res=-1;
        for (register int i=head[now];i!=-1;i=e[i].next)
        if (e[i].to!=fa)
        {
            DFS1(e[i].to,now,d+1);
            size[now]+=size[e[i].to];
            if (size[e[i].to]>res) res=size[e[i].to],son[now]=e[i].to;
        }
    }
    inline void DFS2(int now,int topf)
    {
        id[now]=++tot; s[tot]=a[now];
        top[now]=topf;
        if (!son[now]) return;
        DFS2(son[now],topf);
        for (register int i=head[now];i!=-1;i=e[i].next)
        if (e[i].to!=father[now]&&e[i].to!=son[now]) DFS2(e[i].to,e[i].to);
    }
    inline void change(int x,int y,int z)
    {
        while (top[x]!=top[y])
        {
            if (dep[top[x]]<dep[top[y]]) swap(x,y);
            modify(1,1,n,id[top[x]],id[x],z);
            x=father[top[x]];
        }
        if (dep[x]<dep[y]) swap(x,y);
        modify(1,1,n,id[y],id[x],z);
    }
    inline void updata(int x,int z)
    {
        modify(1,1,n,id[x],id[x]+size[x]-1,z);
    }
    inline LL get_sec(int x,int y)
    {
        LL res=0;
        while (top[x]!=top[y])
        {
            if (dep[top[x]]<dep[top[y]]) swap(x,y);
            res=(res+query(1,1,n,id[top[x]],id[x]))%p;
            x=father[top[x]];
        }
        if (dep[x]<dep[y]) swap(x,y);
        res=(res+query(1,1,n,id[y],id[x]))%p;
        return res;
    }
    inline LL get_son(int x)
    {
        return query(1,1,n,id[x],id[x]+size[x]-1);
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i;
        memset(head,-1,sizeof(head));
        memset(e,-1,sizeof(e));
        read(n); read(q); read(root); read(p);
        for (i=1;i<=n;++i)
        read(a[i]);
        for (i=1;i<n;++i)
        {
            read(x); read(y);
            add(x,y); add(y,x);
        }
        DFS1(root,-1,0);
        DFS2(root,root);
        build(1,1,n);
        while (q--)
        {
            read(opt); read(x);
            if (opt==1) read(y),read(z),change(x,y,z%p);
            if (opt==2) read(y),write(get_sec(x,y)),putchar('
    ');
            if (opt==3) read(z),updata(x,z%p);
            if (opt==4) write(get_son(x)),putchar('
    ');
        }
        return 0;
    }
    

    树剖CODE还是比较长的

    建议大家码的顺序:主程序+其他辅助函数+树剖预处理函数+线段树函数

    一气呵成最重要

    还有另外一道板子题Luogu P2590改一下线段树即可

  • 相关阅读:
    Tomcat中 日志(控制台)中文乱码解决方法
    Maven 编译后 内存中中文数据乱码
    .gitignore无效,不能过滤某些文件
    允许远程用户登录访问mysql的方法
    针对MySQL创建用户后无法登录的原因
    解决Eclipse每次修改完代码后需要先Clean,不然部署不上文件的问题
    Struts2与JQurey ajax配合跨域请求
    Spring 定时任务之 @Scheduled cron表达式
    颜色是这样的,so,status bar和navigation bar颜色是一致的,
    尺寸,误差,
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9010722.html
Copyright © 2011-2022 走看看