最近公共祖先 模板 裸题

最近公共祖先

Time Limit:10000MS  Memory Limit:65536K
Total Submit:75 Accepted:67 
Case Time Limit:1000MS

Description

给出一棵有N(编号1到N)个节点的有根树,求出指定节点对的最近公共祖先! 
 
对于树中节点x而言,从根节点到达x的这一条路径中经过的所有节点,都称为x的祖先。 
如上图所表示的树中, 根节点为8。8、4、10、16都是12的祖先。对于6和12这对节点而言,从6出发往上朝根走和从12出发往上朝根走的两条路径最早交汇的地点是4号节点,因此4号点是6和12的最近公共祖先。 
同理,11和9的最近公共祖先是8; 10和3的最近公共祖先是10;2和7的最近公共祖先是4......

Input

第一行,一个整数N。表示树中节点总数 
接下来N-1行,每行两个整数x和y,表示x是y的父亲。 
接下来一行,一个整数M,表示询问的总数 
接下来M行,每行两个整数a和b,表示询问a和b的最近公共祖先。

Output

M行,每行一个整数,表示对应询问的答案。

Sample Input

输入样例1:
16 
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
3
16 7
14 9
3 10
输入样例2:

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

Sample Output

输出样例1:
4
8
10
输出样例2:
3
3

Hint

2<=N<=10000 
1<=M<=10000

Source

改编自POJ1330


<pre name="code" class="cpp">#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
#define maxn 10009
#define maxm 10009
int dep[maxn], fa[maxn][20], /*BigS 这个变量废弃了*/;
vector <int> edge[maxn];
void fdepfa(int cur)		//计算各点的深度和祖先 
{
	int i, k;
	dep[cur]=dep[fa[cur][0]]+1;	//当前点的深度=父亲节点的深度+1 
	k=ceil(log(dep[cur])/log(2));	//最多网上跳 k 次 
	for(i=1; i<=k; i++)
	{
		fa[cur][i]=fa[fa[cur][i-1]][i-1];
	}
	vector<int>::iterator it;	//深搜儿子节点 
	for(it=edge[cur].begin(); it!=edge[cur].end(); it++)
	{
		fdepfa(*it);
	}
}
int lca(int x, int y)
{
	int i, k, s;
	if(dep[x]<dep[y])	swap(x, y);	//保证x节点始终在y节点之下,即保证x的深度小于y的深度 
	k=dep[x]-dep[y];		//把x跳到和y同一深度需要向上走 k 层 
	//for(i=0; i<=BigS; i++)
	s=ceil(log(dep[x])/log(2));//有这么一天,我把上面那句话改了 
	for(i=0; i<=s; i++)
	{
		if(k&(1<<i))x=fa[x][i];	//要走k步,按位走k步 
	}
	if(x==y)	return x;	//如果x的编号等于y的编号,运气太好,y就是x的祖先 
	s=ceil(log(dep[x])/log(2));	//可惜x和y没有直接祖孙关系,只有共同的祖先,最多往上跳s次 
	for(i=s; i>=0; i--)
		if(fa[x][i]!=fa[y][i])	//从深度小的祖先对照,找到第一个不相同的,直接跳上去 
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	return fa[x][0];
}
int main()
{
	int i;
	int n, m, x, y, a, b;
	scanf("%d", &n);
	//BigS=log(n)/log(2);	//最大倍增深度 ,没什么用 
	for(i=1; i<n; i++)	//只有n-1条边 
	{
		scanf("%d%d", &x, &y);
		fa[y][0]=x;		//y的父亲为x 
		edge[x].push_back(y);	//vector存边 
	}
	for(i=1; i<=n; i++)	if(fa[i][0]==0){fdepfa(i); break;}//从根节点开始计算各点深度,各点的祖先 
	scanf("%d", &m);
	for(i=1; i<=m; i++)
	{
		scanf("%d%d", &a, &b);
		printf("%d\n", lca(a, b));	//lca(a, b)计算a, b的最近公共祖先的编号 
	}
	return 0;
}

 

<pre name="code" class="cpp">#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
#define maxn 10009
#define maxm 10009
int dep[maxn], fa[maxn][20];
vector <int> edge[maxn];
void fdepfa(int cur)
{
	int i, k;
	dep[cur]=dep[fa[cur][0]]+1;
	k=ceil(log(dep[cur])/log(2));
	for(i=1; i<=k; i++)	fa[cur][i]=fa[fa[cur][i-1]][i-1];
	vector<int>::iterator it;
	for(it=edge[cur].begin(); it!=edge[cur].end(); it++)
	{
		fdepfa(*it);
	}
}
int lca(int x, int y)
{
	int i, k, s;
	if(dep[x]<dep[y])	swap(x, y);
	k=dep[x]-dep[y];
	s=ceil(log(dep[x])/log(2));
	for(i=0; i<=s; i++)
		if(k&(1<<i))x=fa[x][i];
	if(x==y)	return x;
	s=ceil(log(dep[x])/log(2));
	for(i=s; i>=0; i--)
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	return fa[x][0];
}
int main()
{
	int i;
	int n, m, x, y, a, b;
	scanf("%d", &n);
	for(i=1; i<n; i++)
	{
		scanf("%d%d", &x, &y);
		fa[y][0]=x;
		edge[x].push_back(y);
	}
	for(i=1; i<=n; i++)	if(fa[i][0]==0){fdepfa(i); break;}
	scanf("%d", &m);
	for(i=1; i<=m; i++)
	{
		scanf("%d%d", &a, &b);
		printf("%d\n", lca(a, b));
	}
	return 0;
}

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值