树链剖分(+线段树)(codevs4633)

Posted z1j1n1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分(+线段树)(codevs4633)相关的知识,希望对你有一定的参考价值。

type node=^link;
  link=record
    des:longint;
    next:node;
     end;

type seg=record
     z,y,lc,rc,toadd,sum:longint;
  end;

var
 n,tot,i,t1,t2,q,a,b,c:longint;
 p:node;
 son,siz,dep,fa,num,top:array[1..100000] of longint;
 tr:array[0..250000] of seg;
 nd:array[1..100000] of node;

 function max(a,b:longint):longint;
   begin
     if a>b then exit(a) else exit(b);
   end;

 function min(a,b:longint):longint;
   begin
     if a>b then exit(b) else exit(a);
   end;

 procedure dfs1(po:longint);
 var
  p:node;
   begin
     siz[po]:=1;son[po]:=0;
     p:=nd[po];
     while p<>nil do
       begin
         if dep[p^.des]=0 then
           begin
             dep[p^.des]:=dep[po]+1;
             fa[p^.des]:=po;
             dfs1(p^.des);
             if siz[p^.des]>siz[son[po]] then
               son[po]:=p^.des;
             siz[po]:=siz[po]+siz[p^.des];
           end;
         p:=p^.next;
       end;
   end;//寻找非叶子结点中儿子siz最大,记录在son中

 procedure dfs2(po,tp:longint);
 var
  p:node;
   begin
     inc(tot);
     num[po]:=tot;
     top[po]:=tp;
     if son[po]<>0 then
       dfs2(son[po],tp);

     p:=nd[po];
     while p<>nil do
       begin
         if (p^.des<>son[po]) and (p^.des<>fa[po]) then dfs2(p^.des,p^.des);
         p:=p^.next;
       end;
   end;//将重边练成重链,num记录树上的点哈希到线段树上的结果

 procedure buildtree(l,r:longint);
 var
  t:longint;
   begin
     inc(tot);
     tr[tot].sum:=0;tr[tot].toadd:=0;
     tr[tot].z:=l;tr[tot].y:=r;
     t:=tot;
     if l=r then exit else
       begin
         tr[t].lc:=tot+1;
         buildtree(l,(l+r) div 2);
         tr[t].rc:=tot+1;
         buildtree((l+r) div 2+1,r);
       end;
   end;//建线段树

  procedure add(po,l,r,k:longint);
  var
   mid:longint;
    begin
      if tr[po].toadd<>0 then
        begin
          tr[po].sum:=tr[po].sum+(tr[po].y-tr[po].z+1)*tr[po].toadd;
          tr[tr[po].lc].toadd:=tr[tr[po].lc].toadd+tr[po].toadd;
          tr[tr[po].rc].toadd:=tr[tr[po].rc].toadd+tr[po].toadd;
          tr[po].toadd:=0;
        end;

      mid:=(tr[po].z+tr[po].y) div 2;
      tr[po].sum:=tr[po].sum+(r-l+1)*k;
      if (l=tr[po].z) and (r=tr[po].y) then
        begin
          tr[tr[po].lc].toadd:=tr[tr[po].lc].toadd+k;
          tr[tr[po].rc].toadd:=tr[tr[po].rc].toadd+k;
          exit;
        end else
          begin
            if mid>=l then add(tr[po].lc,l,min(mid,r),k);
            if r>mid then add(tr[po].rc,max(mid+1,l),r,k);
          end;
    end;//线段树加

  function ans(po,l,r:longint):longint;
  var
   mid:longint;
    begin
      if tr[po].toadd<>0 then
        begin
          tr[po].sum:=tr[po].sum+(tr[po].y-tr[po].z+1)*tr[po].toadd;
          tr[tr[po].lc].toadd:=tr[tr[po].lc].toadd+tr[po].toadd;
          tr[tr[po].rc].toadd:=tr[tr[po].rc].toadd+tr[po].toadd;
          tr[po].toadd:=0;
        end;

      mid:=(tr[po].z+tr[po].y) div 2;
      if (l=tr[po].z) and (r=tr[po].y) then
        exit(tr[po].sum) else
          begin
            ans:=0;
            if mid>=l then ans:=ans+ans(tr[po].lc,l,min(mid,r));
            if r>mid then ans:=ans+ans(tr[po].rc,max(mid+1,l),r);
          end;
    end;//线段树求和

  procedure plus(b,c:longint);
    begin
      while top[b]<>top[c] do
        begin
          if dep[top[b]]<dep[top[c]] then
              begin
                add(1,num[top[c]],num[c],1);
                c:=fa[top[c]];
              end
            else
              begin
                add(1,num[top[b]],num[b],1);
                b:=fa[top[b]];
              end;
        end;
      if num[b]<num[c] then add(1,num[b],num[c],1) else add(1,num[c],num[b],1);
    end;//通过重链寻找被修改的区间

  function query(b,c:longint):longint;
    begin
      query:=0;
      while top[b]<>top[c] do
        begin
           if dep[top[b]]<dep[top[c]] then
              begin
                query:=query+ans(1,num[top[c]],num[c]);
                c:=fa[top[c]];
              end
            else
              begin
                query:=query+ans(1,num[top[b]],num[b]);
                b:=fa[top[b]];
              end;
        end;

      if num[b]<num[c] then query:=query+ans(1,num[b],num[c]) else query:=query+ans(1,num[c],num[b]);
    end;//通过重链寻找被求和的区间

  begin

    read(n);

    for i:=1 to n-1 do
      begin
        read(t1,t2);
        new(p);
        p^.des:=t2;p^.next:=nd[t1];nd[t1]:=p;
        new(p);
        p^.des:=t1;p^.next:=nd[t2];nd[t2]:=p;
      end;

    dep[1]:=1;
    dfs1(1);

    dfs2(1,1);

    tot:=0;
    buildtree(1,n);

    read(q);
    for i:=1 to q do
      begin
        read(a,b,c);

        if a=1 then plus(b,c);

        if a=2 then writeln(query(b,c));
      end;
  end.

 

以上是关于树链剖分(+线段树)(codevs4633)的主要内容,如果未能解决你的问题,请参考以下文章

[CodeVS4633][Mz]树链剖分练习

codevs 4633 [Mz]树链剖分练习

刷题总结——骑士的旅行(bzoj4336 树链剖分套权值线段树)

bzoj 1036 树链剖分+线段树 裸题

BZOJ 2157 旅游(树链剖分+线段树)

BZOJ2157: 旅游 树链剖分 线段树