lca(洛谷P3379 最近公共祖先(LCA))

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

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

输出包含M行,每行包含一个正整数,依次为每一个询问的结果.

输入样例#1:

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

输出样例#1:

4
4
1
4
4思路的话可以见我的转载博文:传送门这是我的代码实现~
%:pragma GCC optmize(3)//o3优化,尽量不要开
#include<bits/stdc++.h>
#define maxn 1100010
#define maxm 1100130
using namespace std;

int n,m,ss;
struct aaa{int to,next;}edge[maxn];

struct bbb{int same,to,next,num;bool flag;bbb(){flag=0;}}s[maxm];

int head[maxn],shead[maxm],pre[maxn],book[maxn],ans[maxm];

int cnt=0;

inline int read()//注意快读
{
	char c=getchar();
	int num=0;
	while(!isdigit(c)) c=getchar();
	while(isdigit(c))
	{
		num=num*10+c-‘0‘;
		c=getchar();
	}
    return num;
} 

void add_edge(int u,int v)
{
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt;
	edge[++cnt].to=u;
	edge[cnt].next=head[v];
	head[v]=cnt;
}

int cntt=0;
void add_search(int u,int v,int w)
{
	s[++cntt].to=v;
	s[cntt].next=shead[u];
	s[cntt].num=w;
	shead[u]=cntt;

	s[++cntt].to=u;
	s[cntt].num=w;
	s[cntt].next=shead[v];
	shead[v]=cntt;
}

int find(int x)
{
	if(pre[x]==x) return x;
	else return pre[x]=find(pre[x]);
}

int merge(int x,int y)//注意谁和谁合并
{
	int fx=find(x),fy=find(y);
	if(pre[fy]!=fx)
	{
		pre[fy]=fx;
		return 1;
	}
	return 0;
}

void tarjan(int point,int f)
{
	for(int i=head[point];i;i=edge[i].next)
		if(edge[i].to!=f&&!book[edge[i].to])//不能返回之前走过的点——父节点
		{
			tarjan(edge[i].to,point);
			merge(point,edge[i].to);
			book[edge[i].to]=1;
		}

	for(int i=shead[point];i;i=s[i].next)
		if(book[s[i].to]&&!s[i].flag)
		{
			ans[s[i].num]=find(s[i].to);
			s[i].flag=1;//走过的点标记
			int tt=i;if(tt%2)tt++;else tt--;
			s[tt].flag=1;//因为是双向储存所以两边都要去掉
		}
}

int main()
{
	scanf("%d%d%d",&n,&m,&ss);
	for(int i=1;i<n;i++)
	{
		int t1,t2;
		t1=read();t2=read();
		add_edge(t1,t2);
	}
	for(int i=1;i<=n;i++)
		pre[i]=i;
	for(int i=1;i<=m;i++)
	{
		int t1,t2;
		t1=read();t2=read();
		add_search(t1,t2,i);
	}
	tarjan(ss,0);//虚构一个父节点
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

  

				
时间: 2024-10-01 03:20:24

lca(洛谷P3379 最近公共祖先(LCA))的相关文章

【C++】最近公共祖先LCA(Tarjan离线算法)&amp;&amp; 洛谷P3379LCA模板

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

求最近公共祖先(LCA)板子 x

LCA目前比较流行的算法主要有tarjian,倍增和树链剖分 1)tarjian 是一种离线算法,需要提前知道所有询问对 算法如下 1.读入所有询问对(u,v),并建好树(建议邻接表) 2.初始化每个节点各属一个并查集,都指向自己 3.对整棵树进行dfs(深度优先搜索)遍历 每处理到一个新节点(u)时看他的另一半(询问对象v)是否visit过,如果visit过了,则这组询问对的lca即v的并查集的根节点,若没有visit过,则继续向下深搜,该节点记为已visit 每当回溯的时候都将子节点的并查集

LCA 算法学习 (最近公共祖先)poj 1330

poj1330 在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点.这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共祖先只有一个.时间复杂度为O(n+q

最近公共祖先(LCA)问题

描述 对于有根树T的两个节点u和v,最近公共祖先LCA(T,u,v)表示一个节点x满足x是u,v的公共祖先且x的深度尽可能大. 算法 求解LCA问题主要有三种解法,分别是暴力搜索,Tanjar算法,最后一种是转化为RMQ问题,用DFS+ST算法来求解 暴力搜索 如果数据量不大的时候可以采用暴力搜索法.先将节点u的祖先节点全部标记出来,然后顺着节点v沿着父亲节点的方向向上遍历,直到遍历到一个被标记的节点,这个节点即为所求节点.或者分别获取u,v到根节点的路径P1,P2,可以将这两条路径看做两个两个

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

洛谷P3379 【模板】最近公共祖先(LCA)

P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有倍- 题面这个地方写错了 无论是用RMQ+dfs还是tarjan- 为什么我的倍增超时了 求助!为什么只有70分 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接

洛谷 P3379 【模板】最近公共祖先(LCA) 如题

P3379 [模板]最近公共祖先(LCA) 时空限制1s / 512MB 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询

【洛谷P3379】【模板】最近公共祖先(LCA)

题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1: 5 5 4 3 1 2 4 5