zoukankan      html  css  js  c++  java
  • [学习笔记] 树链剖分

    今天刚学会树剖。。。。。。(是不是觉得我很菜QwQ)

    树剖的用处:

    引子问题1:

      给你一颗树,支持两种操作:

        1、给x到y路径上的值加z

        2、求出点x的值

      简单,树上差分嘛,前几天刚学过啊。

    引子问题2:

      给你一颗树,支持两种操作:

        1、给以x为根的子树加z

        2、求出以x为根的子树的和。

      简单,dfs序+线段树啊。

    那么把两个问题结合起来呢?——树链剖分华丽丽登场!!!

    树剖核心思想:

    听说线段树挺好用的,区改区查只要log的复杂度,但是只能在线性结构上用,哎,真是太遗憾了。

    听说有一种叫做dfs的东西可以把一棵树转化为线性结构。

    深夜,某dalao拿着这两个东西搞来搞去然后——树链剖分就诞生啦!(好吧是我瞎yy的。。。)

    没错,树剖就是把一棵树分成很多条链,使这些链的dfs序连续,从而把维护线性数据的数据结构搬到树上。

    关于重链和轻链:

    把树分成很多条链,那么应该怎么分呢?树剖发明人把树分为了重链和轻链,重链在dfs中的编号是连续的,也就是说我们可以一次直接从重链的顶端跳到重链的顶端,期间只要用个线段树维护一下就可以了,所以就非常快,诶,这就很优秀,但是轻链的话就只能一个一个慢慢跳,诶,很烦。

    那么他是如何确定哪些链是重链,哪些链是轻链的呢?

    对于树上的一个节点u,取其子树大小最大的儿子作为他的重儿子,那么重儿子和u的连边就是重边,由重边组成的链就是重链。

    这个时候我们可能产生一个疑问:

      不是说重链跳得快吗,那为什么不取子树深度最大的作为中儿子而要取子树规模最大的呢?

    这个问题就问的非常好,很有深度,不经过一定的思考是问不来这个问题的。

    那么这是为什么呢?

    很简单啊其实,注意到轻链只能一个一个跳,非常的凄惨,很慢,那么如果我们取的是子树规模较大的儿子为重儿子,那么一个轻节点就可以少跳几步就跳到一条重链上,然后搭个“顺风车”,诶,这就可以加速很多,而如果取子树深度较大的儿子为重儿子,那么轻节点可能就要多条几次才能跳到,而又优化的只是重链上的节点而已,对于大局而言这就很亏,诶,很不划算。(其实这是我实测出来的。。。)

    树剖主体代码实现:

    procedure dfs1(u,father,dep:longint);
    var
        i,v:longint;
    begin
        depth[u]:=dep; siz[u]:=1;                                //depth:深度,siz:子树规模
        i:=head[u]; faz[u]:=father;                              //faz:父亲
        while i<>0 do
        begin
            v:=vet[i];
            if v<>father then
            begin
                dfs1(v,u,dep+1);
                if siz[v]>siz[son[u]] then son[u]:=v;            //son:重儿子是哪个
                siz[u]:=siz[u]+siz[v];
            end;
            i:=next[i];
        end;
    end;
    procedure dfs2(u,father,t:longint);
    var
        i,v:longint;
    begin
        inc(time); i:=head[u];                                    //dfn:dfs序,top:链顶
        dfn[u]:=time; top[u]:=t;
        if son[u]=0 then exit;
        dfs2(son[u],u,t);                                         //这里先dfs遍历重儿子是为了让重链连续
        while i<>0 do
        begin
            v:=vet[i];
            if (v<>father)and(v<>son[u]) then dfs2(v,u,v);
            i:=next[i];
        end;
    end;

    更新、查询操作:

      更新的时候要把深度大的往上跳,避免出现擦肩而过的尴尬情况。

    procedure update_path(x,y,z:longint);
    var
        fx,fy:longint;
    begin
        fx:=top[x]; fy:=top[y];
        while fx<>fy do
        begin
            if depth[fx]>depth[fy] then                            //选深度大的往上跳
            begin
                update(1,1,time,dfn[fx],dfn[x],z);                 //更新链上的值
                x:=faz[fx];                                        //可以一下跳到链顶,轻链的链顶就是它自己
            end else
            begin
                update(1,1,time,dfn[fy],dfn[y],z);                 //这里的update和下面的query是线段树的更新和查询操作
                y:=faz[fy];
            end;
            fx:=top[x]; fy:=top[y];
        end;
        if x<>y then                                               //感觉这句话没事么用处。。。。。。
            if dfn[x]>dfn[y] then update(1,1,time,dfn[y],dfn[x],z)
            else update(1,1,time,dfn[x],dfn[y],z)
        else update(1,1,time,dfn[x],dfn[y],z);
    end;
    function query_path(x,y:longint):longint;
    var
        fx,fy:longint;
    begin
        fx:=top[x]; fy:=top[y]; query_path:=0;
        while fx<>fy do
        begin
            if depth[fx]>depth[fy] then
            begin
                query_path:=(query_path+query(1,1,time,dfn[fx],dfn[x]))mod p;
                x:=faz[fx];
            end else
            begin
                query_path:=(query_path+query(1,1,time,dfn[fy],dfn[y]))mod p;
                y:=faz[fy];
            end;
            fx:=top[x]; fy:=top[y];
        end;
        if x<>y then
            if dfn[x]>dfn[y] then query_path:=(query_path+query(1,1,time,dfn[y],dfn[x]))mod p
            else query_path:=(query_path+query(1,1,time,dfn[x],dfn[y]))mod p
        else query_path:=(query_path+query(1,1,time,dfn[x],dfn[y]))mod p;
    end;

    树剖模板代码实现:

    题目为洛谷P3384。

    var
        dfn,fin,faz,siz,son,head,depth,top,a:array[0..100000]of longint;
        add_sum,sum:array[0..400000]of longint;
        next,vet:array[0..200000]of longint;
        i,n,m,root,p,x,y,z,time,tot,opt,q:longint;
    function min(a,b:longint):longint;
    begin
        if a<b then exit(a) else exit(b);
    end;
    function max(a,b:longint):longint;
    begin
        if a>b then exit(a) else exit(b);
    end;
    procedure add_(x,y:longint);
    begin
        inc(tot);
        next[tot]:=head[x];
        vet[tot]:=y;
        head[x]:=tot;
    end;
    procedure update(k,l,r,x,y,z:longint);
    var
        mid:longint;
    begin
        if (l>=x)and(r<=y) then
        begin
            add_sum[k]:=(add_sum[k]+z)mod p; exit;
        end;
        sum[k]:=(sum[k]+(min(r,y)-max(l,x)+1)*z mod p)mod p;
        mid:=(l+r)>>1;
        if x<=mid then update(k*2,l,mid,x,y,z);
        if y>mid then update(k*2+1,mid+1,r,x,y,z);
    end;
    function query(k,l,r,x,y:longint):longint;
    var
        mid:longint;
    begin
        if (l>=x)and(r<=y) then exit((sum[k]+(r-l+1)*add_sum[k]mod p)mod p);
        mid:=(l+r)>>1;
        query:=(min(r,y)-max(l,x)+1)*add_sum[k]mod p;
        if x<=mid then query:=(query+query(k*2,l,mid,x,y))mod p;
        if y>mid then query:=(query+query(k*2+1,mid+1,r,x,y))mod p;
    end;
    procedure dfs1(u,father,dep:longint);
    var
        i,v:longint;
    begin
        depth[u]:=dep; siz[u]:=1;
        i:=head[u]; faz[u]:=father;
        while i<>0 do
        begin
            v:=vet[i];
            if v<>father then
            begin
                dfs1(v,u,dep+1);
                if siz[v]>siz[son[u]] then son[u]:=v;
                siz[u]:=siz[u]+siz[v];
            end;
            i:=next[i];
        end;
    end;
    procedure dfs2(u,father,t:longint);
    var
        i,v:longint;
    begin
        inc(time); i:=head[u];
        dfn[u]:=time; top[u]:=t;
        fin[u]:=dfn[u]+siz[u]-1;
        if son[u]=0 then exit;
        dfs2(son[u],u,t);
        while i<>0 do
        begin
            v:=vet[i];
            if (v<>father)and(v<>son[u]) then dfs2(v,u,v);
            i:=next[i];
        end;
    end;
    procedure update_path(x,y,z:longint);
    var
        fx,fy:longint;
    begin
        fx:=top[x]; fy:=top[y];
        while fx<>fy do
        begin
            if depth[fx]>depth[fy] then
            begin
                update(1,1,time,dfn[fx],dfn[x],z);
                x:=faz[fx];
            end else
            begin
                update(1,1,time,dfn[fy],dfn[y],z);
                y:=faz[fy];
            end;
            fx:=top[x]; fy:=top[y];
        end;
        if x<>y then
            if dfn[x]>dfn[y] then update(1,1,time,dfn[y],dfn[x],z)
            else update(1,1,time,dfn[x],dfn[y],z)
        else update(1,1,time,dfn[x],dfn[y],z);
    end;
    function query_path(x,y:longint):longint;
    var
        fx,fy:longint;
    begin
        fx:=top[x]; fy:=top[y]; query_path:=0;
        while fx<>fy do
        begin
            if depth[fx]>depth[fy] then
            begin
                query_path:=(query_path+query(1,1,time,dfn[fx],dfn[x]))mod p;
                x:=faz[fx];
            end else
            begin
                query_path:=(query_path+query(1,1,time,dfn[fy],dfn[y]))mod p;
                y:=faz[fy];
            end;
            fx:=top[x]; fy:=top[y];
        end;
        if x<>y then
            if dfn[x]>dfn[y] then query_path:=(query_path+query(1,1,time,dfn[y],dfn[x]))mod p
            else query_path:=(query_path+query(1,1,time,dfn[x],dfn[y]))mod p
        else query_path:=(query_path+query(1,1,time,dfn[x],dfn[y]))mod p;
    end;
    begin
        read(n,q,root,p);
        for i:=1 to n do
                read(a[i]);
        for i:=1 to n-1 do
        begin
            read(x,y);
            add_(x,y); add_(y,x);
        end;
        dfs1(root,0,1);
        dfs2(root,0,root);
        for i:=1 to n do
            update(1,1,time,dfn[i],dfn[i],a[i]);
        while q>0 do
        begin
            read(opt);
            if opt=1 then
            begin
                read(x,y,z);
                update_path(x,y,z);
            end;
            if opt=2 then
            begin
                read(x,y);
                writeln(query_path(x,y));
            end;
            if opt=3 then
            begin
                read(x,z);
                update(1,1,time,dfn[x],fin[x],z);
            end;
            if opt=4 then
            begin
                read(x);
                writeln(query(1,1,time,dfn[x],fin[x]));
            end;
            dec(q);
        end;
    end.
  • 相关阅读:
    Python 学习笔记 11.模块(Module)
    Python 学习笔记 8.引用(Reference)
    Python 学习笔记 9.函数(Function)
    Python 学习笔记 6.List和Tuple
    Python 学习笔记 4.if 表达式
    Python 学习笔记 2.自省
    Python 学习笔记 3.简单类型
    Python 学习笔记 7.Dictionary
    Python 学习笔记 5.对象驻留
    Python 学习笔记 10.类(Class)
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/9901085.html
Copyright © 2011-2022 走看看