1. 程式人生 > 其它 >樹鏈剖分求lca

樹鏈剖分求lca

題目描述
給一棵有根樹,以及一些詢問,每次詢問樹上的2 個節點A、B,求它們的最近公共祖先.

輸入

第一行一個整數N.接下來N 個數,第i 個數Fi 表示i 的父親是Fi. 若Fi = 0,則i 為樹根.
接下來一個整數M.接下來M 行,每行2 個整數A、B,詢問節點(A xor LastAns)、(Bxor LastAns)的最近公共祖先. 其中LastAns 為上一個詢問的答案,一開始LastAns = 0.
輸出
對每一個詢問輸出相應的答案.

樣例輸入
10
0 1 2 3 2 4 2 5 4 9
10
3 9
2 7
7 8
1 1
0 6
6 11
6 3
10 7
2 15
7 7
樣例輸出
3
1
4
5
2
4
2
5
2
5

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 5e5 + 5;
vector<int>map[maxn];
int vis[maxn];
int fa[maxn];
int Size[maxn];
int Mainson[maxn];
int depth[maxn];
int root;

void DFS1(int v)
{
	vis[v] = 1; Size[v] = 1;
	for (int i = 0; i < map[v].size(); i++) {
		int u = map[v][i];
		if (!vis[u]) {
			fa[u] = v;
			depth[u] = depth[v] + 1;
			DFS1(u);
			Size[v] += Size[u];
			if (Size[u] > Size[Mainson[v]])Mainson[v] = u;
		}
	}
}

int path[maxn];
void DFS2(int u)
{
	vis[u] = 1;
	for (int i = 0; i < map[u].size(); i++) {
		int v = map[u][i];
		if (!vis[v]) {
			if (v == Mainson[u])path[v] = path[u];
			else path[v] = v;
			DFS2(v);
		}
	}
}

int lca(int a, int b)
{
	while (path[a] != path[b]) {
		if (depth[path[a]] > depth[path[b]])a = fa[path[a]];
		else b = fa[path[b]];
	}
	return depth[a] < depth[b] ? a : b;
}
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		int a; scanf("%d", &a);
		if (a == 0)root = i;
		else {
			map[i].push_back(a); map[a].push_back(i);
		}
	}
	memset(vis, 0, sizeof(vis)); depth[root] = 0;
	DFS1(root);
	memset(vis, 0, sizeof(vis)); path[root] = root;
	DFS2(root);
	int m;scanf("%d", &m);
	int lastans = 0;
	//for (int i = 1; i <= n; i++)printf("%d\n", depth[i]);
	for (int i = 1; i <= m; i++) {
		int a, b;
		scanf("%d %d", &a, &b);
		int ans = lca(a ^ lastans, b ^ lastans);
		printf("%d\n", ans);
		lastans = ans;
	}
}