codechef Lowest Common Ancestor

Lowest Common Ancestor

Problem code: TALCA

All submissions for this problem are available.

Read problems statements in Mandarin Chinese.

 

In a rooted tree, the lowest common ancestor (or LCA for short) of two vertices u and v is defined as the lowest vertex that is ancestor of both that two vertices.

Given a tree of N vertices, you need to answer the question of the form "r u v" which means if the root of the tree is at r then what is LCA of u and v.

 

Input

The first line contains a single integer N. Each line in the next N - 1 lines contains a pair of integer u andv representing a edge between this two vertices.

The next line contains a single integer Q which is the number of the queries. Each line in the next Q lines contains three integers r, u, v representing a query.

 

Output

For each query, write out the answer on a single line.

Constraints

20 points:

  • 1 ≤ NQ ≤ 100

 

40 points:

  • 1 ≤ NQ ≤ 105
  • There is less than 10 unique value of r in all queries

 

40 points:

  • 1 ≤ NQ ≤ 2 × 105

 

Example

Input:
4
1 2
2 3
1 4
2
1 4 2
2 4 2

Output:
1
2
 
    
 
    
LCA算法,二分
</pre><pre name="code" class="cpp">/*题意:给定一颗N个点的树,有Q个询问,每次询问以r为根的u与v的LCA。
先以0号点为root dfs一遍,并得出anc数组。
一个重要的发现:u v两点的LCA一定在u->v这条路径上。易知LCA点一定是与root距离最近的点,
所以我们可以二分u点到LCA的距离,最终确定答案。*/
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

#define MAXL 19
#define MAXN 200005
#define MAXQ 200005

int N, Q;
int a, b;
int r[MAXQ];
int u[MAXQ];
int v[MAXQ];

int dep[MAXN];
int dad[MAXN]; //dad[i]表示i号点的父亲编号
vector< int > T[MAXN]; //邻接表
int anc[MAXN][MAXL]; //anc[i][j]表示i号点向上2^j步所到达的点

int sol[MAXQ];

vector< int > q, nq;

void dfs(int n, int d = 0) { //得到父子关系
	dep[n] = d;
	for (int i = 0; i < T[n].size(); ++i) {
		if (T[n][i] == dad[n]) continue;
		dad[T[n][i]] = n;
		dfs(T[n][i], d + 1);
	}
}

void make_root(int r) {//计算anc数组
	dad[r] = -1;
	dfs(r);

	for (int i = 0; i < N; ++i)
		anc[i][0] = dad[i];

	for (int j = 1; j < MAXL; ++j) 
		for (int i = 0; i < N; ++i)
			anc[i][j] = anc[anc[i][j - 1]][j - 1];
}

int get(int n, int jump) { //快速得到n向上跳跃jump步所到达的点
	for (int i = MAXL - 1; i >= 0; --i) 
		if (((jump >> i) & 1) == 1)
			n = anc[n][i];
	return n;
}

int lca(int a, int b) { //求a与b的LCA
	if (dep[a] < dep[b]) swap(a, b);
	a = get(a, dep[a] - dep[b]);

	if (a == b) return a;

	for (int i = MAXL - 1; i >= 0; --i) {
		if (anc[a][i] != anc[b][i]) {
			a = anc[a][i];
			b = anc[b][i];
		}
	}

	return dad[a];
}

int kth(int a, int b, int k) { //a向上跳k步,若超出lca了,剩下的步数往b方向跳
	int L = lca(a, b);

	if (dep[a] - dep[L] >= k) 
		return get(a, k);

	k -= dep[a] - dep[L] ;

	return get(b, dep[b] - dep[L] - k );
}

int dist(int a, int b) { //计算a与b的距离
	int L = lca(a, b);
	return dep[a] + dep[b] - 2 * dep[L];
}

using namespace std;

int main(void)
{
	scanf("%d", &N);

	for (int i = 0; i < N - 1; ++i) {
		scanf("%d%d", &a, &b); --a; --b;
		T[a].push_back(b);
		T[b].push_back(a);
	}

	make_root(0);
	scanf("%d", &Q);

	for (int i = 0; i < Q; ++i) {
		int R, U, V;
		scanf("%d%d%d", &R, &U, &V); --R; --U; --V;

		int lo = 0, hi = dist(U, V), mid;

		while (lo < hi) { //二分距离
			mid = (lo + hi) / 2;
			if (dist(kth(U, V, mid), R) > dist(kth(U, V, mid + 1), R))
				lo = mid + 1;
			else
				hi = mid;
		}

		printf("%d\n", kth(U, V, lo) + 1);
	}

	return 0;
}


 
   



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值