zoukankan      html  css  js  c++  java
  • 树链剖分之轻重链讲解

      首先我们有一颗树每个点(或者边)有权值,我们要做的就是询问两个点之间路径上各点(边)权值的最大、最小,权值和(就是线段树能干的),然后我们还要支持在线更改任意节点(边)的权值。

      我们要做的是轻重链剖分,首先我们看几个定义

      size:和SBT里的一样,size[i]为以该点为根节点的子树一共有几个节点。

      重儿子:一个节点当不为叶子节点的时候有且只有一个重儿子,重儿子为该点的儿子中size最大的,有多个最大时任选一个。

      重链:由根节点开始,每个点每次都访问自己的重儿子,一直访问到叶子节点,就组成了一条重链

      那么对于一个点的非重儿子来说,以他为根节点,可以重新访问出一条重链

      如图所示,用红色的线画出的为重链,其中6号点自己为一条重链,那么对于每条重链,我们需要记下他的顶标top,就是该重链中深度最小的节点的标号

      比如链1-3-4-9-10,的top为1,链2-8的top为2。

      重链几个明显的性质就是互不重合且所有重链覆盖所有点,重链之间由一条不在重链上的边(我们称作轻边)连接,然后对于每一条重链来说,我们定义他的深度,顶标为根节点的重链的深度为1,顶标的父亲在深度为x的重链上,那么该重链深度为x+1,如图链1-3-4-9-10的深度为1,链2-8,链5-7的深度为2,链6的深度为3。 

    那么我们首先DFS,可以求出每个点的size,然后再深搜一遍可得到每个点的top,和处理出每一条链

    procedure make(x,t,depth:longint);//x代表当前节点,t代表当前重链的顶标,detph代表该重链的深度
    
    var
    
    begin
    
        if max_son[x]<>0 then make(max_son,top,depth);
    
        q:=last[x];
    
        while q<>0 do
    
        begin
    
            p:=other[q];
    
            if (not flag[p]) and (p<>max_son[x]) then
    
            begin
    
                flag[p]:=true;
    
                make(p,p,depth+1);
    
            end;
    
            q:=pre[q];
    
        end;
    
    end.
    
     

      那么我们现在有了所有的重链,覆盖每一个点,然后我们要处理的问题是两点之间的最值等问题。

      是不是有些像线段树,假设我们需要求一条重链上的最大值,那么我们需要将重链存进线段树,且重链上的所有点的编号是连续的(区间),那么我们要对所有的点以深搜序重新编号(make的时候顺便搞一下就好了),那么我们可以用线段树来存树上点的值(边儿的也一样,可以将边下放到点),这样对于在同一条重链上的点我们就可以在logn的时间里求出值了,然后对于不同链上的点,我们先给他们升到同一深度上的链,同时更新答案,然后做就好了(有点类似于倍增,就是今年noip Day1 T3,其实内道题用

    树链剖分也行,应该早点学啊啊啊啊)至于每个点的权值修改就完全是线段树的事儿了。

     第一次写,代码不是很好

    //By BLADEVIL
    type
        rec                 =record
            sum, max        :longint;
            left, right     :longint;
        end;
     
    var
        n                   :longint;
        pre, other          :array[0..60010] of longint;
        last, key           :array[0..30010] of longint;
        l                   :longint;
        father, size        :array[0..30010] of longint;
        flag                :array[0..30010] of boolean;
        max_son             :array[0..30010] of longint;
        tot                 :longint;
        num                 :array[0..30010] of longint;
        dep, top            :array[0..30010] of longint;
        t                   :array[0..200010] of rec;
        a                   :array[0..30010] of longint;
         
    function max(a,b:longint):longint;
    begin
        if a>b then max:=a else max:=b;
    end;
     
    procedure swap(var a,b:longint);
    var
        c                   :longint;
    begin
        c:=a; a:=b; b:=c;
    end;
         
    procedure connect(x,y:longint);
    begin
        inc(l);
        pre[l]:=last[x];
        last[x]:=l;
        other[l]:=y;
    end;
         
    procedure dfs(x:longint);
    var
        p, q                :longint;
         
    begin
        q:=last[x];
        size[x]:=1;
        while q<>0 do
        begin
            p:=other[q];
            if not flag[p] then
            begin
                father[p]:=x;
                flag[p]:=true;
                dfs(p);
                inc(size[x],size[p]);
                if size[max_son[x]]<size[p] then max_son[x]:=p;
            end;
            q:=pre[q];
        end;
    end;
         
    procedure make(x,t,depth:longint);
    var
        q, p                :longint;
    begin
        inc(tot);
        num[x]:=tot;
        a[tot]:=key[x];
        top[x]:=t;
        dep[x]:=depth;
        if max_son[x]<>0 then
        begin
            flag[max_son[x]]:=true;
            make(max_son[x],t,depth);
        end;
        q:=last[x];
        while q<>0 do
        begin
            p:=other[q];
            if (not flag[p]) and (p<>max_son[x]) then
            begin
                flag[p]:=true;
                make(p,p,depth+1);
            end;
            q:=pre[q];
        end;
    end;
         
    procedure build(x,l,r:longint);
    var
        mid                 :longint;
    begin
        t[x].left:=l; t[x].right:=r;
        if l=r then
        begin
            t[x].sum:=a[l];
            t[x].max:=a[l];
            exit;
        end;
        mid:=(r+l) div 2;
        build(2*x,l,mid); build(2*x+1,mid+1,r);
        t[x].sum:=t[x*2].sum+t[x*2+1].sum;
        t[x].max:=max(t[x*2].max,t[x*2+1].max);
    end;
         
    function get_max(x,ll,rr:longint):longint;
    var
        mid                 :longint;
    begin
        if (t[x].left=ll) and (t[x].right=rr) then
        begin
            get_max:=t[x].max;
            exit;
        end;
        with t[x] do mid:=(left+right) div 2;
        if mid>=rr then get_max:=get_max(x*2,ll,rr) else
        if mid<ll then get_max:=get_max(x*2+1,ll,rr) else
        get_max:=max(get_max(x*2,ll,mid),get_max(x*2+1,mid+1,rr));
    end;
     
    function get_sum(x,ll,rr:longint):longint;
    var
        mid                 :longint;
    begin
        get_sum:=0;
        if (t[x].left=ll) and (t[x].right=rr) then
        begin
            get_sum:=t[x].sum;
            exit;
        end;
        with t[x] do mid:=(left+right) div 2;
        if mid>=rr then get_sum:=get_sum(x*2,ll,rr) else
        if mid<ll then get_sum:=get_sum(x*2+1,ll,rr) else
        get_sum:=get_sum(x*2,ll,mid)+get_sum(x*2+1,mid+1,rr);
    end;
     
    procedure change(x,u,v:longint);
    var
        mid                 :longint;
    begin
        if (t[x].left=u) and (t[x].right=u) then
        begin
            t[x].sum:=v;
            t[x].max:=v;
            exit;
        end;
        with t[x] do mid:=(right+left) div 2;
        if u>mid then change(x*2+1,u,v) else change(x*2,u,v);
        t[x].sum:=t[x*2].sum+t[x*2+1].sum;
        t[x].max:=max(t[x*2].max,t[x*2+1].max);
    end;
         
    procedure init;
    var
        i                   :longint;
        x, y                :longint;  
    begin
        read(n);
        for i:=1 to n-1 do
        begin
            read(x,y);
            connect(x,y);
            connect(y,x);
        end;
        for i:=1 to n do read(key[i]);
        flag[1]:=true;
        dfs(1);
        fillchar(flag,sizeof(flag),false);
        flag[1]:=true;
        make(1,1,1);
        build(1,1,n);
    end;
     
    procedure query_max(x,y:longint);
    var
        ans                 :longint;
    begin
        ans:=-maxlongint div 10;
        if dep[x]>dep[y] then swap(x,y);
        while dep[x]<dep[y] do
        begin
            ans:=max(ans,get_max(1,num[top[y]],num[y]));
            y:=father[top[y]];
        end;
        while top[x]<>top[y] do
        begin
            ans:=max(ans,get_max(1,num[top[x]],num[x]));
            ans:=max(ans,get_max(1,num[top[y]],num[y]));
            x:=father[top[x]];
            y:=father[top[y]];
        end;
        x:=num[x]; y:=num[y];
        if x>y then swap(x,y);
        ans:=max(ans,get_max(1,x,y));
        writeln(ans);
    end;
     
    procedure query_sum(x,y:longint);
    var
        ans                 :longint;
    begin
        ans:=0;
        if dep[x]>dep[y] then swap(x,y);
        while dep[x]<dep[y] do
        begin
            inc(ans,get_sum(1,num[top[y]],num[y]));
            y:=father[top[y]];
        end;
        while top[x]<>top[y] do
        begin
            inc(ans,get_sum(1,num[top[x]],num[x]));
            inc(ans,get_sum(1,num[top[y]],num[y]));
            x:=father[top[x]];
            y:=father[top[y]];
        end;
        x:=num[x]; y:=num[y];
        if x>y then swap(x,y);
        inc(ans,get_sum(1,x,y));
        writeln(ans);
    end;
     
    procedure main;
    var
        i                   :longint;
        m                   :longint;
        s                   :ansistring;
        c                   :char;
        x, y                :longint;
         
    begin
        readln(m);
        for i:=1 to m do
        begin
            c:='w';
            s:='';
            while c<>' ' do
            begin
                read(c);
                s:=s+c;
            end;
            readln(x,y);
            if s[2]='M' then
                query_max(x,y) else
            if s[2]='S' then
                query_sum(x,y) else
                change(1,num[x],y);
        end;
    end;
         
         
    begin
        init;
        main;
    end.
  • 相关阅读:
    React
    移动端
    Flask 框架小记
    PyTorch 学习
    CNN 小结
    Django 环境下常用的模型设计
    Linux用户和用户组
    Linux下查看进程的命令输出的内容解释
    linux下配置tomcat开机自启动
    商业智能概述
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3479713.html
Copyright © 2011-2022 走看看