[BZOJ1787][Ahoi2008]Meet 紧急集合&&[BZOJ1832][AHOI2008]聚会

传送门

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

题目大意

给定一棵树,每次询问3个点到某一点的最小距离和

题解

3个点,其实任意两点间有lca,而且最多有3个不同lca(事实上最多是2个),这样的话3种情况比大小即可
事实是:3个点两两lca最多有两个不同lca,集合节点为不同于其他两个的那个点
这个我们yy一下就好了...(T_T)

const
 maxn=500005;
var
 w:array[0..3*maxn,1..2]of longint;
 dep,t:array[0..maxn]of longint;
 st:array[0..maxn,0..20]of longint;
 i,j,k:longint;
 n,m,len,a,b,c,d,e,f,v,tt,head,tail,maxd,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;

function lca(a,b:longint):longint;
var i:longint;
begin
 if dep[a]>dep[b] then begin i:=a; a:=b; b:=i; end;
 for i:=trunc(ln(maxd)/ln(2))+1 downto 0 do
  if dep[st[b,i]]>=dep[a]
  then b:=st[b,i];
 if a=b then exit(a);
 for i:=trunc(ln(maxd)/ln(2))+1 downto 0 do
  if st[a,i]<>st[b,i]
  then begin a:=st[a,i]; b:=st[b,i]; end;
 exit(st[a,0]);
end;

function dis(a,b:longint):longint;
var k:longint;
begin
 k:=lca(a,b);
 exit(dep[a]+dep[b]-dep[k]*2);
end;

begin
 readln(n,m); len:=n+1;
 for i:=1 to n-1 do
  begin
   readln(a,b);
   init(a,b); init(b,a);
  end;
 fillchar(dep,sizeof(dep),0);
 dep[1]:=1; t[1]:=1; head:=1; tail:=2; maxd:=1;
 while head<tail do
  begin
   v:=t[head]; inc(head); tt:=w[v,2];
   while tt<>0 do
    begin
     if dep[w[tt,1]]=0 then begin
      dep[w[tt,1]]:=dep[v]+1; t[tail]:=w[tt,1]; inc(tail);
      st[w[tt,1],0]:=v;
      if dep[v]+1>maxd then maxd:=dep[v]+1;
     end;
     tt:=w[tt,2];
    end;
  end;
 for j:=1 to trunc(ln(maxd)/ln(2))+1 do
  for i:=1 to n do
   st[i,j]:=st[st[i,j-1],j-1];
 for i:=1 to m do
  begin
   readln(a,b,c);
   d:=lca(a,b); e:=lca(a,c); f:=lca(b,c);
   if d=e then k:=f
   else if e=f then k:=d
   else k:=e;
   ans:=dis(a,k)+dis(b,k)+dis(c,k);
   writeln(k,' ',ans);
  end;
end.

你可能感兴趣的:([BZOJ1787][Ahoi2008]Meet 紧急集合&&[BZOJ1832][AHOI2008]聚会)