jzoj3555 树的直径

Description

科学家在观测一棵大树,这棵树在不断地生长,科学家给这棵树的每个节点编了号。开始的时候,这棵树很小只有4个节点,一号点为根,其他三个节点挂在上面。
在接下来的M次观察中,科学家每次都能看见这棵树从叶子处长出新的两个节点来。如果当前这棵树有N个节点,那么这棵树的新的两个节点的编号分别为N+1,N+2。科学家记录下了这棵树生长的过程,需要你帮着计算这棵树实时的直径。树的直径就是这棵树最远的两个节点的距离。

Input

第一行一个整数M,代表观察的次数。
接下来M行,每行一个整数x,代表这棵树的编号为x的节点下面又长了两个叶子节点。保证每次生长的节点都是叶子节点。

Output

M行,每次生长后这棵树的直径。

Sample Input

5
2
3
4
8
5

Sample Output

3
4
4
5
6

Data Constraint

对于10%的数据,N<=10
对于40%的数据,N<=1000
对于100%的数据,N<=100000。

Solution

首先要发现一个性质,新直径的两端必定在有一个端点在旧直径上。
之后就可以先离线把所有点都增加好,建一棵树,记录一下每一轮新增加的点是哪两个。
然后倍增一下求lca再算两点路径就可以了。

Code(Pascal)

type new=record
  x,y:longint;
end;
var  n,i,j,l1,r1,l,r,s,tot,x,cnt,s1,t:longint;
     h,fa,next,head,b:array [0..250000] of longint;
     anc:array [0..200500,0..18] of longint;
     a:array [0..100050] of new;
procedure add(x,y:longint);
begin
  inc(tot);
  next[tot]:=head[x];
  head[x]:=tot;
  b[tot]:=y;
end;
procedure dfs(x:longint);
var  i,j:longint;
begin
  anc[x,0]:=fa[x];
  for i:=1 to 18 do anc[x,i]:=anc[anc[x,i-1]][i-1];
  i:=head[x];
  while i<>0 do begin
    j:=b[i];
    if j<>fa[x] then begin
      fa[j]:=x;
      h[j]:=h[x]+1;
      dfs(j);
    end;
    i:=next[i];
  end;
end;
function lca(x,y:longint):longint;
var     i:longint;
begin
  if h[x]<h[y] then begin
    i:=x; x:=y; y:=i;
  end;
  for i:=18 downto 0 do if h[anc[x,i]]>=h[y] then x:=anc[x,i];
  if x=y then exit(x);
  for i:=18 downto 0 do
    if (anc[x,i]<>anc[y,i]) then begin
      x:=anc[x,i];
      y:=anc[y,i];
    end;
  exit(anc[x,0]);
end;
begin
  readln(n);
  add(1,2);
  add(1,3);
  add(1,4);
  cnt:=4;
  for i:=1 to n do begin
   read(x);
   inc(cnt);
   a[i].x:=cnt;
   add(x,cnt);
   inc(cnt);
   a[i].y:=cnt;
   add(x,cnt);
  end;
  fa[1]:=1;
  h[1]:=1;
  dfs(1);
  l:=2; r:=4; s:=2;
  for i:=1 to n do begin
    t:=lca(l,a[i].x);
    s1:=h[l]+h[a[i].x]-2*h[t];
    if s1>s then begin
      l1:=l; r1:=a[i].x;
      s:=s1;
    end;
    t:=lca(r,a[i].x);
    s1:=h[r]+h[a[i].x]-2*h[t];
    if s1>s then begin
      l1:=a[i].x; r1:=r;  s:=s1;
    end;
    t:=lca(l,a[i].y);
    s1:=h[l]+h[a[i].y]-2*h[t];
    if s1>s then begin
      l1:=l; r1:=a[i].y; s:=s1;
    end;
    t:=lca(r,a[i].y);
    s1:=h[r]+h[a[i].y]-2*h[t];
    if s1>s then begin
      l1:=r; r1:=a[i].y; s:=s1;
    end;
    writeln(s);
    l:=l1; r:=r1;
  end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值