树链剖分

前些天提到了GDKOI的事情,严sir说这周六之前要自学一下树链剖分,然后我就去看了一下,这一下就是一周……(某LJ好像看了一周多才搞定)然后ZZX大神讲课的时候我就意识到我的实现方法在细节上严重受到hzwer大神污染……反正大体上是一样的。

感谢hzwer大神提供了代码和详细解答:http://hzwer.com/2543.html

其实树链剖分的意思就是把一棵树里每条边的情况放到线段树上。所谓的剖分就是指把树里面一条条的链分成轻链和重链两类进行操作。

重链的值用线段树维护查询,轻边的值直接查询。

然后的话要DFS2遍,第一遍先求出每个点的深度dep,子树大小size,以及parent(这里的parent[x,i]指点x往上2^i的祖先)

第二遍的话求重链,就是用往下拓展的方式把重链拉出来。选择子树较大的节点继承重链,其余子节点再自己往下拉重链。

然后就用和普通线段树类似的方式维护就可以了,不过比较特殊的一点是其修改方式如下:

1、单独修改一个点的权值

根据其编号直接在数据结构中修改就行了。

2、修改点u和点v的路径上的权值

(1)若u和v在同一条重链上

直接用数据结构修改pos[u]至pos[v]间的值。

(2)若u和v不在同一条重链上

一边进行修改,一边将u和v往同一条重链上靠,然后就变成了情况(1)。

这里就是我的实现方式(或者说hzwer大神​实现方式)的不同之处了,zzx的实现方式是每次把深度较深的点往上靠。我的方式是直接求出lca再往lca上靠……

查询的思路也是一样的。

好吧,上代码:(codevs2460)

type map=record
         t,next:longint;
         end;
     tpoint=^tppoint;
     tppoint=record
             l,r,m:longint;
             num,max:longint;
             lc,rc:tpoint;
             end;
var n,q,m,i,j,num,x,y,root,k,pa:longint;
    parent:array[1..30000,0..14]of longint;
    value,head,dep,size,belong,p:array[0..30000]of longint;
    vis:array[1..30000]of boolean;
    edge:array[1..60000]of map;
    tree:tpoint;
    s,order:string;
function max(a,b:longint):longint;
begin
  if a>b then exit(a) else exit(b);
end;
procedure swap(var a,b:longint);
var t:longint;
begin
  t:=a;
  a:=b;
  b:=t;
end;
procedure insert(s,t:longint);
begin
  inc(m);
  edge[m].t:=t;
  edge[m].next:=head[s];
  head[s]:=m;
  inc(m);
  edge[m].t:=s;
  edge[m].next:=head[t];
  head[t]:=m;
end;
procedure dfs1(x:longint);
var i:longint;
begin
  size[x]:=1;
  vis[x]:=true;
  for i:=1 to 14 do
  begin
    if dep[x]<1 shl i then break;
    parent[x,i]:=parent[parent[x,i-1],i-1];
  end;
  i:=head[x];
  while i>0 do
  begin
    if vis[edge[i].t] then
    begin
      i:=edge[i].next;
      continue;
    end;
    dep[edge[i].t]:=dep[x]+1;
    parent[edge[i].t,0]:=x;
    dfs1(edge[i].t);
    inc(size[x],size[edge[i].t]);
    i:=edge[i].next;
  end;
end;
procedure dfs2(x,chain:longint);
var i,k:longint;
begin
  k:=0;
  inc(num);
  p[x]:=num;
  belong[x]:=chain;
  i:=head[x];
  while i>0 do
  begin
    if (dep[edge[i].t]>dep[x])and(size[edge[i].t]>size[k]) then
    k:=edge[i].t;
    i:=edge[i].next;
  end;
  if k=0 then exit;
  dfs2(k,chain);
  i:=head[x];
  while i>0 do
  begin
    if (dep[edge[i].t]>dep[x])and(k<>edge[i].t) then
    dfs2(edge[i].t,edge[i].t);
    i:=edge[i].next;
  end;
end;
function lca(x,y:longint):longint;
var t,i:longint;
begin
  if dep[x]<dep[y] then swap(x,y);
  t:=dep[x]-dep[y];
  for i:=0 to 14 do
  if t and(1 shl i)>0 then
  x:=parent[x,i];
  for i:=14 downto 0 do
  if parent[x,i]<>parent[y,i] then
  begin
    x:=parent[x,i];
    y:=parent[y,i];
  end;
  if x=y then exit(x)else exit(parent[x,0]);
end;
procedure build(var p:tpoint;l,r:longint);
begin
  new(p);
  p^.l:=l;
  p^.r:=r;
  p^.m:=(l+r)div 2;
  if l=r then
  begin
    p^.lc:=nil;
    p^.rc:=nil;
    p^.num:=0;
    p^.max:=0;
    exit;
  end;
  build(p^.lc,l,p^.m);
  build(p^.rc,p^.m+1,r);
  p^.num:=0;
  p^.max:=0;
end;
procedure update(var p:tpoint;x,y:longint);
begin
  if p^.l=p^.r then
  begin
    p^.max:=y;
    p^.num:=y;
    exit;
  end;
  if x<=p^.m then update(p^.lc,x,y)
  else update(p^.rc,x,y);
  p^.num:=p^.lc^.num+p^.rc^.num;
  p^.max:=max(p^.lc^.max,p^.rc^.max);
end;
function asknum(p:tpoint;x,y:longint):longint;
begin
  if p=nil then exit(0);
  if (p^.l=x)and(p^.r=y) then
  exit(p^.num);
  if y<=p^.m then exit(asknum(p^.lc,x,y))
  else if x>p^.m then exit(asknum(p^.rc,x,y))
  else exit(asknum(p^.lc,x,p^.m)+asknum(p^.rc,p^.m+1,y));
end;
function askmax(p:tpoint;x,y:longint):longint;
begin
  if p=nil then exit(0);
  if (p^.l=x)and(p^.r=y) then
  exit(p^.max);
  if y<=p^.m then exit(askmax(p^.lc,x,y))
  else if x>p^.m then exit(askmax(p^.rc,x,y))
  else exit(max(askmax(p^.lc,x,p^.m),askmax(p^.rc,p^.m+1,y)));
end;
function solve1(x,pa:longint):longint;
var res:longint;
begin
  res:=0;
  while belong[x]<>belong[pa] do
  begin
    inc(res,asknum(tree,p[belong[x]],p[x]));
    x:=parent[belong[x],0];
  end;
  inc(res,asknum(tree,p[pa],p[x]));
  exit(res);
end;
function solve2(x,pa:longint):longint;
var mx:longint;
begin
  mx:=-maxlongint;
  while belong[x]<>belong[pa] do
  begin
    mx:=max(mx,askmax(tree,p[belong[x]],p[x]));
    x:=parent[belong[x],0];
  end;
  mx:=max(mx,askmax(tree,p[pa],p[x]));
  exit(mx);
end;
begin
  readln(n);
  for i:=1 to n-1 do
  begin
    readln(x,y);
    insert(x,y);
  end;
  for i:=1 to n do
  read(value[i]);
  num:=0;
  dfs1(1);
  dfs2(1,1);
  build(tree,1,n);
  for i:=1 to n do
  update(tree,p[i],value[i]);
  readln(q);
  for i:=1 to q do
  begin
    readln(s);
    order:=copy(s,1,2);
    k:=pos(' ',s);
    delete(s,1,k);
    k:=pos(' ',s);
    val(copy(s,1,k-1),x);
    delete(s,1,k);
    val(s,y);
    if order[1]='C' then
    begin
      value[x]:=y;
      update(tree,p[x],y);
    end
    else
    begin
      root:=lca(x,y);
      if order[2]='M' then
      writeln(max(solve2(x,root),solve2(y,root)))
      else
      writeln(solve1(x,root)+solve1(y,root)-value[root]);
    end;
  end;
end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值