bzoj 3631 树链剖分+差分

题意:n个节点的树,给出访问顺序,每经过一个点该点权值加1(x->y->z,y只需要加一次),初始为0.求最后每个点经过总次数

显然树剖裸题

区间加1,单点查询,所以完全没有必要写线段树,一开始naive的想用差分的树状数组代替线段树,后来一想都差分了还树状数组个什么鬼,直接差分一下,然后算一下前缀和即可

如果当前点是所有访问的起点,直接输出sum[i],其他点均-1

var
        n,l,tot,x,y     :longint;
        i               :longint;
        a,sum           :array[0..300010] of longint;
        last            :array[0..300010] of longint;
        pre,other       :array[0..600010] of longint;
        vis             :array[0..300010] of boolean;
        d,top,num,father:array[0..300010] of longint;
        max_son,size    :array[0..300010] of longint;

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

procedure change(x,y:longint);
begin
   inc(sum[x]);
   dec(sum[y+1]);
end;

procedure work(x,y:longint);
begin
   if d[x]>d[y] then swap(x,y);
   while d[x]<d[y] do
   begin
      change(num[top[y]],num[y]);
      y:=father[top[y]];
   end;
   while top[x]<>top[y] do
   begin
      change(num[top[x]],num[x]);
      change(num[top[y]],num[y]);
      x:=father[top[x]];
      y:=father[top[y]];
   end;
   if num[x]<num[y] then change(num[x],num[y]) else change(num[y],num[x]);
end;

begin
   read(n);
   for i:=1 to n do read(a[i]);
   for i:=1 to n-1 do
   begin
      read(x,y);
      connect(x,y);
      connect(y,x);
   end;
   vis[a[1]]:=true; dfs(a[1]);
   fillchar(vis,sizeof(vis),false);
   vis[a[1]]:=true; make(a[1],1,1);
   for i:=1 to n-1 do work(a[i],a[i+1]);
   for i:=1 to n do sum[i]:=sum[i-1]+sum[i];
   for i:=1 to n do
     if i=a[1] then writeln(sum[num[i]]) else writeln(sum[num[i]]-1);
end.
——by Eirlys



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值