[BZOJ1782] [Usaco2010 Feb]slowdown 慢慢游

传送门

http://www.lydsy.com/JudgeOnline/problem.php?id=1782

题目大意

给定一棵n个点的树,每次从1出发到达a[i],询问到达a[i]之后,经过了几个之前已经到达的点

题解

简单画下图我们就能发现,每次走完就相当于给a[i]的子树权值(之后到达某个点的答案)+1,为了维护这个,我们用DFS序把子树维护到一条线段上,来回答每次询问
我们需要支持区间修改,单点查询的数据结构,很明显线段树可以,但是我想介绍一下树状数组的写法
树状数组我们知道只能单点修改,区间查询(前缀和),所以这里我们用到差分序列来维护,对于[L,R],单点修改L节点使其加一,R+1节点减一

const
 maxn=100010;
var
 y:array[0..maxn,1..2]of longint;
 t,x,z:array[0..maxn]of longint;
 w:array[0..4*maxn,1..2]of longint;
 i,j,k:longint;
 n,len,a,b,ans:longint;
procedure init(a,b:longint);
begin
 w[len,1]:=b;
 if w[a,2]=0
 then w[a,2]:=len else w[w[a,1],2]:=len;
 w[a,1]:=len; inc(len);
end;

procedure dfs(a:longint);
var tt:longint;
begin
 inc(len); y[a,1]:=len; z[a]:=len;
 tt:=w[a,2];
 while tt<>0 do
  begin
   k:=y[w[tt,1],1];
   if y[w[tt,1],1]=0
   then dfs(w[tt,1]);
   tt:=w[tt,2];
  end;
 y[a,2]:=len;
end;

procedure update(a,b:longint);
begin
 while a<=n do
  begin
   inc(t[a],b);
   inc(a,a and(-a));
  end;
end;

function query(a:longint):longint;
var k:longint;
begin
 k:=0;
 while a>0 do
  begin
   inc(k,t[a]);
   dec(a,a and(-a));
  end;
 exit(k);
end;

begin
 readln(n); len:=n+1;
 for i:=1 to n-1 do
  begin
   readln(a,b);
   init(a,b); init(b,a);
  end;
 for i:=1 to n do
  readln(x[i]);
 len:=0;
 dfs(1);
 for i:=1 to n do
  begin
   ans:=query(z[x[i]]);
   update(y[x[i],1],1); update(y[x[i],2]+1,-1);
   writeln(ans);
  end;
end.

你可能感兴趣的:([BZOJ1782] [Usaco2010 Feb]slowdown 慢慢游)