树链剖分(+线段树)(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.
时间: 2024-10-31 02:32:55

树链剖分(+线段树)(codevs4633)的相关文章

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj3589】动态树 树链剖分+线段树

题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u,

BZOJ2243 (树链剖分+线段树)

Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. 解题分析 树链剖分+线段树. 开一个记录类型,记录某一段区间的信息.l 表示区间最左侧的颜色 , r 表示区间最右侧的颜色 , sum 表示区间中颜色段数量. 合并时判断一下左区间的右端点和有区间的左端点的颜色是否一样. 树上合并时需要用两个变量ans1,ans2来存储.ans1表示x往上走时形成的链的信息,

bzoj4304 (树链剖分+线段树)

Problem T2 (bzoj4304 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 解题分析 练手题.树链剖分+线段树. 参考程序 1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #incl

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

HDU 2460 Network(双连通+树链剖分+线段树)

HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链剖分+线段树处理 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #pragma comment(linke

【bzoj1959】[Ahoi2005]LANE 航线规划 离线处理+树链剖分+线段树

题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一些先遣飞船已经出发,在星球之间开辟探险航线. 探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线. 例如下图所示: 在5个星球之间,有5条探险航

HDU4897 (树链剖分+线段树)

Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作2:将所有 有且仅有一个点在a->b的路径中 的边的颜色翻转. 操作3:询问a->b的路径中的黑色边数量. 解题分析 考虑操作1,只需正常的树链剖分+线段树维护即可.用线段树维护每条边,tag_1[i]表示该区间中的黑色边数量. 考虑操作2,一个节点相邻的边只可能为重链和轻链,且重链的数目小于等于