lca倍增算法

HDU2586

要注意只有简单边相连的图是一棵树,树有n-1条边

倍增之前要先存双向边,以任意顶点为根(比如1)dfs遍历确定树。

倍增前求f[i,j]时先赋值为-1便于判断f[ij-1]祖先存不存在

倍增时先将两个点调至同一高度(注意最后的微调每次只上升一个高度)

program hdu2586;
const maxm=15;
var i,j,ans,t,l1,l,n,m,x,y,z:longint;
    f,g:array[0..40000,0..15]of longint;
    vis:array[0..40000]of boolean;
    edge:array[1..80000,1..3]of longint;
    deep:array[-1..40000]of longint;
    head:array[1..40000]of longint;
procedure dfs(u,de:longint);//buildtree_root=1
var p:longint;
begin
 vis[u]:=true;
 deep[u]:=de;
 p:=head[u];
 while p<>-1 do
  begin
   if vis[edge[p,1]]=false then
    begin
     g[edge[p,1],0]:=edge[p,2];
     f[edge[p,1],0]:=u;
     dfs(edge[p,1],de+1);
    end;
   p:=edge[p,3];
  end;
end;

procedure make;//倍增
var j,i:longint;
begin
 for j:=1 to maxm do
  for i:=1 to n do
    if f[i,j-1]<>-1 then
     begin
      f[i,j]:=f[f[i,j-1],j-1];
      g[i,j]:=g[i,j-1]+g[f[i,j-1],j-1];
     end;
end;
procedure find(x,y:longint);
var tmp,i:longint;
begin
 if deep[x]<deep[y] then
  begin
   tmp:=x;x:=y;y:=tmp;
  end;
 for i:=maxm downto 1 do
  if deep[f[x,i]]>deep[y] then
   begin
    ans:=ans+g[x,i];
    x:=f[x,i];
   end;
 while deep[x]>deep[y] do
  begin
   ans:=ans+g[x,0];x:=f[x,0];
  end;
 if x=y then exit;
 for i:=maxm downto 1 do
  if f[x,i]<>f[y,i] then
   begin
    ans:=ans+g[x,i]+g[y,i];
    x:=f[x,i];
    y:=f[y,i];
   end;
 while x<>y do
  begin
   ans:=ans+g[x,0]+g[y,0];
   x:=f[x,0];
   y:=f[y,0];
  end;
end;
begin
 read(t);
 for l1:=1 to t do
  begin
   read(n,m);
   for i:=1 to n do head[i]:=-1;
   for i:=1 to n-1 do
    begin
     read(x,y,z);
     l:=l+1;
     edge[l,1]:=y;
     edge[l,2]:=z;
     edge[l,3]:=head[x];
     head[x]:=l;
     l:=l+1;
     edge[l,1]:=x;
     edge[l,2]:=z;
     edge[l,3]:=head[y];
     head[y]:=l;
    end;
   fillchar(f,sizeof(f),0);//father
   fillchar(g,sizeof(g),0);//distence
   fillchar(vis,sizeof(vis),false);
   for j:=0 to maxm do
    for i:=1 to n do
     f[i,j]:=-1;//f[1,0]:=-1;g[1,0]:=0;
   dfs(1,1);
   make;
   for i:=1 to m do
    begin
     ans:=0;
     read(x,y);
     find(x,y);
     writeln(ans);
    end;
  end;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值