bzoj 1036 树链剖分模板

61 篇文章 0 订阅
5 篇文章 0 订阅

题意:一棵n个节点的树,每个点都有一个权值w,三种操作:

1、CHANGE x y 把x节点的权值改为y

2、QMAX x y 询问x到y路径上节点的最大权值

3、QSUM x y 询问x到y路径的节点和,包括x和y本身


树链剖分裸模板..

简单介绍下树链剖分

一般用来维护两个点之间路径上各点(边)权值的最大、最小,权值和并且支持在线更改任意节点(边)的权值(把链剖出来挂在线段树上)

明确一些定义:

size:size[i]为以该点为根节点的子树的节点数。

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

重链:由根节点开始,每个点每次都访问自己的重儿子,一直访问到叶子节点,就组成了一条重链。对于一个点的非重儿子来说,以他为根节点,可以重新访问出一条重链

top:重链中深度最小的节点

链的深度:定义根节点的深度为1,一条重链的深度为father[top]所在重链的深度+1

显然,一棵树就被剖成了若干条链互不重合的链

用两次dfs维护

第一遍(dfs):得到size和每个点重儿子

第二遍(make):形成若干条重链,并得到相应的top和深度,因为要挂在线段树中维护,所以每个点按照dfs的顺序得到一个在线段树中对应的编号

剩下的交给线段树维护即可

对于两个点的询问,如果在同一条链上,线段数求出答案,如果不在同一条链上,先把他们升到同一条链上(类似lca的方法,但lca用的是倍增,而这里是每次处理一条链)同时更新答案

对于修改,要注意修改的是x节点在线段树中的位置,所以修改的时候上传的编号是在线段树中的该节点编号

type
    rec=record
        l,r,max,sum:longint;
end;

var
    n,l,m,x,y,tot       :longint;
    i                   :longint;
    ch                  :char;
    s                   :string;
    vis                 :array[0..30010] of boolean;
    last,w,father,a,num :array[0..30010] of longint;
    size,top,d,max_son  :array[0..30010] of longint;
    pre,other           :array[0..60010] of longint;
    t                   :array[0..90010] of rec;
function max(a,b:longint):longint;
begin
   if a<b then exit(b) else exit(a);
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 build(x,l,r:longint);
var
    mid:longint;
begin
   t[x].l:=l; t[x].r:=r;
   if (l=r) then
   begin
      t[x].max:=a[l]; t[x].sum:=a[l];
      exit;
   end;
   mid:=(l+r) >>1;
   build(2*x,l,mid); build(2*x+1,mid+1,r);
   t[x].max:=max(t[x*2].max,t[2*x+1].max);
   t[x].sum:=t[2*x].sum+t[2*x+1].sum;
end;

function get_max(x,l,r:longint):longint;
var
    mid:longint;
begin
   if (t[x].l=l) and (t[x].r=r) then exit(t[x].max);
   mid:=(t[x].l+t[x].r) >>1;
   if (r<=mid) then exit(get_max(2*x,l,r)) else
     if (l>mid) then exit(get_max(2*x+1,l,r)) else
       exit(max(get_max(2*x,l,mid),get_max(2*x+1,mid+1,r)));
end;

function get_sum(x,l,r:longint):longint;
var
    mid:longint;
begin
   if (t[x].l=l) and (t[x].r=r) then exit(t[x].sum);
   mid:=(t[x].l+t[x].r)>>1;
   if (r<=mid) then exit(get_sum(2*x,l,r)) else
     if (l>mid) then exit(get_sum(2*x+1,l,r)) else
       exit(get_sum(2*x,l,mid)+get_sum(2*x+1,mid+1,r));
end;

procedure change(x,y,z:longint);
var
    mid:longint;
begin
   if (t[x].l=y) and (t[x].r=y) then
   begin
      t[x].sum:=z;
      t[x].max:=z;
      exit;
   end;
   mid:=(t[x].l+t[x].r)>>1;
   if (y<=mid) then change(2*x,y,z) else change(2*x+1,y,z);
   t[x].max:=max(t[2*x].max,t[2*x+1].max);
   t[x].sum:=t[2*x].sum+t[2*x+1].sum;
end;

procedure dfs(x:longint);
var
    p,q:longint;
begin
   size[x]:=1;
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not vis[p] then
      begin
         vis[p]:=true;
         father[p]:=x;
         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
    p,q:longint;
begin
   inc(tot);
   num[x]:=tot;
   a[tot]:=w[x];
   top[x]:=t;
   d[x]:=depth;
   if max_son[x]<>0 then
   begin
      vis[max_son[x]]:=true;
      make(max_son[x],t,depth);
   end;
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not vis[p] and (p<>max_son[x]) then
      begin
         vis[p]:=true;
         make(p,p,depth+1);
      end;
      q:=pre[q];
   end;
end;

procedure ask_max(x,y:longint);
var
    ans:longint;
begin
   ans:=-maxlongint;
   if d[x]>d[y] then swap(x,y);
   while (d[x]<d[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;
   if num[x]>num[y] then ans:=max(ans,get_max(1,num[y],num[x])) else ans:=max(ans,get_max(1,num[x],num[y]));
   writeln(ans);
end;

procedure ask_sum(x,y:longint);
var
    ans:longint;
begin
   ans:=0;
   if d[x]>d[y] then swap(x,y);
   while (d[x]<d[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;
   if num[x]>num[y] then inc(ans,get_sum(1,num[y],num[x])) else inc(ans,get_sum(1,num[x],num[y]));
   writeln(ans);
end;

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(w[i]);
   vis[1]:=true; dfs(1);
   fillchar(vis,sizeof(vis),false);
   vis[1]:=true; make(1,1,1);
   build(1,1,n);
   readln(m);
   for i:=1 to m do
   begin
      s:='';
      read(ch);
      while (ch<>' ') do
      begin
         s:=s+ch;
         read(ch);
      end;
      if s='CHANGE' then
      begin
         readln(x,y);
         change(1,num[x],y);
      end else
      if s='QMAX' then
      begin
         readln(x,y);
         ask_max(x,y);
      end else
      begin
         readln(x,y);
         ask_sum(x,y);
      end;

   end;
end.
——by Eirlys



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值