树链剖分

树链剖分

浅谈树链剖分

树链剖分是分块的思想
将树上所有边分成轻重边,把重边连成重链,把轻边单独维护,重边连成的链放在数据结构中,根据两点间重链和轻边数量的限制,使得时间复杂度能够令人满意

一些定义

  • 重儿子:在 u 为父亲的所有儿子中子树节点和最大的儿子
  • 轻儿子:除重儿子以外的儿子
  • 重边:父亲与重儿子的连边
  • 轻边:父亲与轻儿子的连边
  • 重链:把重边从上向下?连成一条链

一些数组

  • fa[i]:i的父节点
  • top[i]:i 所在的链中深度最小的节点,若为轻儿子top[i]即为自己
  • dep[i]:i 的深度
  • son[i]:i 的重儿子
  • pos[i]:i 的点/父边在线段树(数据结构)中的位置,即点 i /边i所在线段树的区间 [pos[i],pos[i]]

一条性质

  • uv,dize[v]<=size[u]2
  • 从根到任意一点的路径上,轻边与重链数量都不超过 logN

具体过程

  1. 第一次 dfs fa[],dep[],size[],son[] 求出来
  2. 第二次 dfs pos[],top[] 求出来
    • 对于 top[i],top[son[i]]=top[i] ,轻边 top[i]=i
    • dfs 的时候重边先行,这样能够使重边在线段树中是一整段区间
  3. 对于两点间路径的操作
    • 对于 (u,v) 两点间的路径,每次比较 top[u] top[v] ,取深度值较大的点(假设为 top[v]) ,修改 vtop[v] 间的权值,然后一直到 u v在一条重链上停止
    • 最后把 u v两点间修改

时空复杂度

时间复杂度:每次走 logN 级别的轻边和重链,线段树上修改重边是 logN 级别,总复杂度为 O(Qlog2N)
空间复杂度:线段树的复杂度 O(NlogN)

一些例题

  • [BZOJ1036] [ZJOI2008]树的统计Count
    • 题目大意:支持1.单点修改2.查询路径点权最大值3.查询点权路径和
    • 题解:树链剖分模板入门题
  • [POJ3237]树的维护
    • 题目大意:支持1.单边修改2.修改路径边权取相反数3.查询路径边权最大值
    • 题解:把边权转化为点权,根的点权设为一个影响查询的值
  • [SPOJ375]Query on a tree
    • 题目大意:支持1.单边修改2.查询两点间最大边权值
    • 题解:就是上一道的弱化版嘛
  • [BZOJ4196] [Noi2015]软件包管理器
    • 题目大意:支持1.树链修改+查询2.子树修改+查询
    • 题解:树链剖分包含DFS序
  • [BZOJ2243] [SDOI2011]染色
    • 题目大意:支持1.树链染色2.查询树链颜色段数
    • 题解:树剖后线段树维护区间左端点颜色右端点颜色和颜色段数,合并自己yy以下就好了,两条链向上合并时注意细节

一些总结

  • 树链剖分是一种思想,核心还是把用在其他数据结构的技巧搬过来
  • 树链剖分也是DFS建立线段树,所以它是DFS序的增强版本,DFS序的性质(子树)树链剖分原生支持
  • 树链剖分 (u,v) 向上寻找时,最终两个点 (u,v) ,中 dep 较小的点为 lca(u,v)

一套模板

参考[BZOJ1036] [ZJOI2008]树的统计Count

const
    maxn=30010;
type
    data=record sum,max:longint; end;
var
    w:array[0..3*maxn,1..2]of longint;
    seg:array[0..8*maxn,1..4]of longint; {seg[i,3]:sum; seg[i,4]:max}
    pos,val,size,top,son,fa,dep:array[0..maxn]of longint;
    i,j,k:longint;
    n,m,len,a,b:longint;
    cha,ch:char;
function mmax(a,b:longint):longint;
begin if a>b then exit(a) else exit(b); end;
procedure init(a,b:longint);
begin
    w[len,1]:=b;
    if w[a,2]=0 then w[a,2]:=len else w[w[a,1],2]:=len;
    w[a,1]:=len; inc(len);
end;
procedure build(a,l,r:longint);
var mid:longint;
begin
    seg[a,1]:=l; seg[a,2]:=r; seg[a,3]:=0; seg[a,4]:=0;
    if l=r then exit;
    mid:=(l+r)>>1;
    build(a<<1,l,mid); build(a<<1+1,mid+1,r);
end;
procedure update(a,b,c:longint);
var mid:longint;
begin
    if seg[a,1]=seg[a,2] then begin seg[a,3]:=c; seg[a,4]:=c; exit; end;
    mid:=(seg[a,1]+seg[a,2])>>1;
    if b<=mid
    then update(a<<1,b,c) else update(a<<1+1,b,c);
    seg[a,3]:=seg[a<<1,3]+seg[a<<1+1,3]; seg[a,4]:=mmax(seg[a<<1,4],seg[a<<1+1,4]);
end;
function query(a,l,r:longint):data;
var mid:longint; ans,t1,t2:data;
begin
    if (seg[a,1]=l)and(seg[a,2]=r) then begin ans.sum:=seg[a,3]; ans.max:=seg[a,4]; exit(ans); end;
    mid:=(seg[a,1]+seg[a,2])>>1;
    if r<=mid then exit(query(a<<1,l,r)) else
    if l>mid then exit(query(a<<1+1,l,r))
    else begin t1:=query(a<<1,l,mid); t2:=query(a<<1+1,mid+1,r); ans.sum:=t1.sum+t2.sum; ans.max:=mmax(t1.max,t2.max); exit(ans); end;
end;
procedure dfs1(a:longint); {size[],son[],fa[],dep[]}
var tt,v:longint;
begin
    tt:=w[a,2]; size[a]:=1; v:=0;
    while tt<>0 do
        begin
            if w[tt,1]<>fa[a]
            then
                begin
                    fa[w[tt,1]]:=a; dep[w[tt,1]]:=dep[a]+1;
                    dfs1(w[tt,1]);
                    inc(size[a],size[w[tt,1]]); if size[w[tt,1]]>size[v] then v:=w[tt,1];
                end;
            tt:=w[tt,2];
        end;
    son[a]:=v;
end;
procedure dfs2(a,pre:longint); {top[],pos[]}
var tt:longint;
begin
    tt:=w[a,2]; top[a]:=pre; inc(len); pos[a]:=len; update(1,len,val[a]);
    if son[a]<>0 then dfs2(son[a],pre);
    while tt<>0 do
        begin
            if (w[tt,1]<>fa[a])and(w[tt,1]<>son[a]) then dfs2(w[tt,1],w[tt,1]);
            tt:=w[tt,2];
        end;
end;
function queryans(a,b:longint):data;
var tt:longint; t1,ans:data;
begin
    ans.sum:=0; ans.max:=-maxlongint;
    while top[a]<>top[b] do
        begin
            if dep[top[a]]<dep[top[b]] then begin tt:=a; a:=b; b:=tt; end;
            t1:=query(1,pos[top[a]],pos[a]);
            inc(ans.sum,t1.sum); ans.max:=mmax(ans.max,t1.max);
            a:=fa[top[a]];
        end;
    if dep[a]>dep[b] then begin tt:=a; a:=b; b:=tt; end;
    t1:=query(1,pos[a],pos[b]);
    inc(ans.sum,t1.sum); ans.max:=mmax(ans.max,t1.max);
    exit(ans);
end;
begin
    readln(n); len:=n+1;
    for i:=1 to n-1 do
        begin
            readln(a,b);
            init(a,b); init(b,a);
        end;
    for i:=1 to n do
        read(val[i]);
    dep[1]:=1; fa[1]:=0; size[0]:=0;
    dfs1(1);
    build(1,1,n); len:=0;
    dfs2(1,1);
    readln(m);
    for i:=1 to m do
        begin
            read(cha,ch);
            case ch of
            'H':begin readln(cha,cha,cha,cha,a,b); val[a]:=b; update(1,pos[a],b)3B end;
            'S':begin readln(cha,cha,a,b); writeln(queryans(a,b).sum); end;
            'M':begin readln(cha,cha,a,b); writeln(queryans(a,b).max); end;
            end;
        end;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值