LCA-tarjan找最近公共祖先

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式:
第一行包含三个正整数N , M , S N,M,SN,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。接下来N − 1 N-1N−1行每行包含两个正整数x , y x,yx,y,表示x xx结点和y yy结点之间有一条直接连接的边(数据保证可以构成树)。接下来M MM行每行包含两个正整数a , b a,ba,b,表示询问a aa结点和b bb结点的最近公共祖先。

输出格式:
输出包含M MM行,每行包含一个正整数,依次为每一个询问的结果。

#include <iostream>
using namespace std;
const int N = 10010;
struct Edge
{
	int next;
	int to;
}edge[2 * N];
struct Tar
{
	int next;
	int to;
	int num;
}tar[2 * N];
int head[2 * N];                    //以i节点的第一条边
int tarHead[2 * N];           
int cnt,cnt_tar;
int deep[N];
int fa[N][20];                     //公共祖先模板
int s[N*2];                        //并查集
bool vis[N*2];                    //回溯标记
int ans[N * 2];                   //记录公共祖先

void init()
{
	for (int i = 0; i < 2 * N; i++)   //初始化
	{
		edge[i].next = -1; head[i] = -1;
		tar[i].next = -1; tarHead[i] = -1;
	}
	cnt = 0; cnt_tar = 0;
}
void addEdge(int u, int v)        //初始化链式向前星
{
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}
void addTar(int u, int v,int t)  //初始化要求的公共祖先节点关系
{
	tar[cnt_tar].to = v;
	tar[cnt_tar].next = tarHead[u];
	tar[cnt_tar].num = t;
	tarHead[u] = cnt_tar++;
}

int find_set(int x)        // 找到公共祖先
{
	return x == s[x] ? x : find_set(s[x]);
}
void Tarjan(int x)      //后序遍历dfs树,找到当前树的公共祖先关系
{
	vis[x] = true;
	for (int i = head[x]; ~i; i = edge[i].next)  
	{
		int y = edge[i].to;
		if (!vis[y])
		{
			Tarjan(y);
			s[y] = x;
		}
	}

	for (int i = tarHead[x]; ~i; i = tar[i].next)
	{
		int z = tar[i].to;
		if (vis[z])
		{
			ans[tar[i].num] = find_set(z);
		}
	}
}
void solve()
{
	init();
	int n, m, root;
	cin >> n >> m >>root;
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i < n; i++)
	{
		int u, v;
		s[i] = i;
		cin >> u >> v;
		addEdge(u, v);
		addEdge(v, u);

	}

	s[n] = n;
	for (int i = 1; i <=m; i++)
	{
		int u,v;
		cin >> u >> v;
		addTar(u,v,i);
		addTar(v, u, i);

	}
	Tarjan(root);

	for (int i = 1; i <= m; i++)
		cout<< ans[i] << endl;
}
unsigned main()
{
	ios::sync_with_stdio(false);
	int num = 1;
	while(num--)
	solve();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值