POJ1986 Distance Queries

本文介绍了如何利用Tarjan算法解决树结构中的最近公共祖先问题,详细阐述了算法原理和应用实例,包括离线Tarjan算法、在线O(n^2)-O(1)算法及离线的+1RMQ算法,并提供了求解过程的Pascal代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本题中题目叙述没有给出,但是测试数据中默认的条件为:本题的图必为一棵树!否则是不可能在1s的时间内实现的。

知道了这是一棵树,那么下面的问题就简单了:我们只需要求出询问的两个点的LCA。

求LCA有三种算法:一种是离线Tarjan算法,一种是在线O(n^2)-O(1)算法,还有一种是离线的+-1RMQ算法。

我采用的是Tarjan算法,唯一的缺点是非递归方法太难,pascal要加编译开关,否则会导致栈溢出。

下面贴一段Tarjan算法求LCA的pascal代码:

Procedure Tarjan(i:Longint);
var j,k:Longint;
begin
        j:=last[i];
        while j<>0 do
                begin
                        k:=other[j];
                        if k<>father[i] then
                                begin
                                        father[k]:=i;
                                        Tarjan(k);
                                        Union(i,k);
                                        color[Root(i)]:=i;
                                end;
                        j:=pre[j];
                end;
        v[i]:=true;
        j:=la[i];
        while j<>0 do
                begin
                        k:=ot[j];
                        if v[k] then LCA[i,k]:=color[Root(k)];
                        j:=pr[j];
                end;
end;

下面是程序:

{$M 100000000}
Program POJ1986;//By_Poetshy
Const
        maxn=40005;
Var
        i,j,k,m,n                       :Longint;
        kset,dist,father                :Array[0..maxn]of Longint;
        pre,other,last,long             :Array[0..maxn*2]of Longint;
        pr,ot,la,ans,color              :Array[0..maxn]of Longint;
        v                               :Array[0..maxn]of Boolean;
        p,q                             :Longint;


Procedure Dfs(i:Longint);
var j,k:Longint;
begin
        j:=last[i];
        while j<>0 do
                begin
                        k:=other[j];
                        if not v[k] then
                                begin
                                        dist[k]:=dist[i]+long[j];
                                        v[k]:=true;
                                        Dfs(k);
                                end;
                        j:=pre[j];
                end;
end;

Function Root(i:Longint):Longint;
begin
	if kset[i]=i then exit(i);
	kset[i]:=Root(kset[i]);
	exit(kset[i]);
end;

Procedure Union(i,j:Longint);
var x,y:Longint;
begin
        x:=root(i);y:=root(j);
        if x=y then exit else kset[x]:=y;
end;

Procedure Tarjan(i:Longint);
var j,k,p,q:Longint;
begin
	j:=last[i];
	while j<>0 do
		begin
			k:=other[j];
			if father[i]<>k then
				begin
                                        father[k]:=i;
                                        Tarjan(k);
                                        Union(i,k);
                                        Color[Root(i)]:=i;
                                end;
                        j:=pre[j];
		end;
	v[i]:=true;
	j:=la[i];
	while j<>0 do
		begin
			k:=ot[j];
                        if k=i then
                                begin
                                        ans[j]:=0;
                                        if odd(j) then ans[j+1]:=0 else ans[j-1]:=0;
                                end else
			if v[k] then ans[j]:=dist[i]+dist[k]-2*dist[Color[Root(k)]];
			j:=pr[j];
		end;
end;

BEGIN
        readln(n,m);
        for i:=1 to m do
                begin
                        readln(p,q,j);
                        inc(k);pre[k]:=last[p];last[p]:=k;other[k]:=q;long[k]:=j;
                        inc(k);pre[k]:=last[q];last[q]:=k;other[k]:=p;long[k]:=j;
		end;
        for i:=1 to n do kset[i]:=i;
        readln(m);k:=0;
        for i:=1 to m do
                begin
                        readln(p,q);
                        inc(k);pr[k]:=la[p];la[p]:=k;ot[k]:=q;
                        inc(k);pr[k]:=la[q];la[q]:=k;ot[k]:=p;
                end;
        fillchar(v,sizeof(v),0);
        dist[1]:=0;v[1]:=true;
        Dfs(1);
	fillchar(v,sizeof(v),0);
	father[1]:=0;
        Tarjan(1);
	for i:=1 to m do
	        if ans[i<<1]=0 then ans[i*2]:=ans[i*2-1] else ans[i*2-1]:=ans[i<<1];
	for i:=1 to m do writeln(ans[i*2]);
END.


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值