最近公共祖先问题(LCA)-Tarjan算法

Tarjan算法的实现有很多方法,这里我们记录的是并查集维护下的Tarjan离线算法

【离线算法】指基于在执行算法前输入数据已知的基本假设,也就是说,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。

例题-POJ.1470-Closest Common Ancestors

首先献上一个TLE了的代码,我还不知道到底怎么会TLE了的。
希望有个大神能帮忙看一下。如果你是新手,请跳过这段代码继续向下看。

#include
#include
#include
#include
#include
using namespace std;

class edge{
public:
	int v,nxt;
	edge(){};
	edge(int x,int y):v(x),nxt(y){};
};
class query{
public:
	int x,y;
	bool ok;
	query(int a,int b):x(a),y(b),ok(false){};
	bool operator< (const query& rhs)const{return x psd;
int fa[maxn];
int father[maxn];
edge mapper[maxn];
int head[maxn];
int vis[maxn];
int ans[maxn];
int cnt;

void init()
{
	for(int i=0;i<=n;i++) fa[i]=father[i]=i;
	int p = n+3;
	memset(head,-1,sizeof(head));
	memset(ans,0,sizeof(ans));
	memset(vis,0,sizeof(vis));
	psd.clear();
	cnt=0;
}

void add(int u,int v)
{
	mapper[cnt] = edge(u,head[v]);
	head[v] = cnt++;
}

int readin()
{
	//cout<<"nump is "<

下面是一个AC的代码(这个代码写得还是很不错的)

#include
#include
#include
#include
using namespace std;

const int maxn = 1004;
//采用maxn*maxn的大小
struct node{//放置树
	int u,v,next;
} g[maxn*maxn];
struct nod{//放置问题
	int u,v,next;
} G[maxn*maxn];

int n,m;
int head[maxn],hd[maxn];
int tot;
int res[maxn];
int vis[maxn],pre[maxn],fa[maxn];

void init()
{
	tot=0;
	memset(vis,0,sizeof(vis));
	memset(fa,-1,sizeof(fa));
	memset(res,0,sizeof(res));//清空答案数组
	memset(head,-1,sizeof(head));
	memset(hd,-1,sizeof(hd));
	for(int i=1;i<=n;i++) pre[i]=i;
}

void addg(int u,int v)
{
	g[tot].v = v;
	g[tot].next = head[u];
	head[u] = tot++;
}

void addG(int u,int v)
{
	G[tot].v = v;
	G[tot].next = hd[u];
	hd[u] = tot++;
}

int Find(int x)
{
	return (x==pre[x])? x : pre[x]=Find(pre[x]);
}

//核心函数
void lca(int u,int fa)
{//这里加入上一级的父亲节点fa其实是为了适配无向路
	for(int i=head[u];~i;i=g[i].next)
	{
		int v=g[i].v;
		if(v==fa)continue;//用来避免无向路带来的dfs死循环
		if(!vis[v])
		{
			lca(v,u);
			pre[v]=u;
		}
	}
	vis[u]=1;//一定要在u点的子树搜索完成后才能标记
	for(int i=hd[u];~i;i=G[i].next)
	{//这里将询问也按照邻接表的形式进行保存
		int v=G[i].v;
		if(vis[v]) res[Find(v)]++;
	}
	//vis[u]=1 也可以写在这里
}

int main()
{
	while(~scanf("%d",&n))
	{
		init();
		int a,b,c;
		for(int i=0;i

然后是我参考的几篇相对好一些的文章

文章1

文章2

文章3


LCA核心函数伪代码模板

vis[s]:s是否被访问的标记-初值是False
Father[s]:s的父亲节点-初值是s
CommonAncestor[a,b]:指a,b两节点的最近公共祖先
Querys:所有询问的集合

/*Find是并查集的维护代码*/
Find(x){return (x==father[x])?x:father[x]=Find(father[x]);}

LCA(vertex,father):
BEGIN
	1、FOR u OF ALL SONs NODE OF vertex:
			IF u == father :
				conintue;
			IF vis[u] == False :
				LCA(u,vertex);
				Father[u]=vertex;
	2、vis[u]=True;
	3、FOR query(u,t) IN Querys:
			IF vis[t]==True:
				CommonAncestor[a,b] = Find(t);
END

OK

你可能感兴趣的:(最近公共祖先问题(LCA)-Tarjan算法)