樹鏈剖分之輕重鏈講解


  首先我們有一顆樹每個點(或者邊)有權值,我們要做的就是詢問兩個點之間路徑上各點(邊)權值的最大、最小,權值和(就是線段樹能干的),然后我們還要支持在線更改任意節點(邊)的權值。

  我們要做的是輕重鏈剖分,首先我們看幾個定義

  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.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM