洛谷 P3379 (LCA Tarjan && 倍增)

題目連接

題意:

           N 個點 ,N - 1條邊,M 個詢問每個尋問 兩個整數X, Y。  求 X,  Y 的LCA (最近公共祖先), S點是根節點

思路:

            LCA模板題

LCA Tarjan AC :

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 5e5+ 10;
int fa[MAXN], head[MAXN], head_ask[MAXN], cnt, cnt_ask, ans[MAXN];
bool vis[MAXN];
int n, m, s;
struct Edge{
	int to, dis, next;
	int num;
}edge[MAXN << 1], ask[MAXN << 1]; 
void add_edge(int u, int v, int dis) {
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void add_ask(int x, int y, int num) { //num 第幾個查詢
	ask[++cnt_ask].to = y;
	ask[cnt_ask].num = num;	//第幾個查詢 
	ask[cnt_ask].next = head_ask[x];
	head_ask[x] = cnt_ask;
}
int find(int x) {	//並查集 
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void init() {
	cnt = 1;
	cnt_ask = 1;
	memset(vis, 0, sizeof(vis));
	fa[n] = n;
}
void Tarjan(int x) {
	vis[x] = true;
	for(int i = head[x]; i ; i = edge[i].next) {
		int to = edge[i].to;
		if( !vis[to] ) {
			Tarjan(to);
			fa[to] = x;
		}
	}
	for(int i = head_ask[x]; i; i = ask[i].next) {
		int to = ask[i].to;
		if( vis[to] ){ 
			ans[ask[i].num] = find(to);
		}
	}
}
int main () {
	ios::sync_with_stdio(0);
    cin.tie(0);
	cin >> n >> m >> s;
	int x, y;
	init();
	for(int i = 1; i < n; ++i) {
		fa[i] = i;
		cin >> x >> y;
		add_edge(x, y, 0);
		add_edge(y, x, 0);
	}
	for(int i = 1; i <= m; ++i) {
		cin >> x >> y;
		add_ask(x, y, i);
		add_ask(y, x, i);
	} 
	Tarjan(s);	 
	for(int i = 1; i <= m; ++i) {
		cout << ans[i] << endl;
	}
} 

LCA 倍增 AC :

#include <bits/stdc++.h>
using namespace std;
#define P pair<int, int>
const int MAXN = 500000 + 5;
int N, M, S;
int head[MAXN], cnt;
vector<P> ask;
int fa[MAXN][20], depth[MAXN];
struct Edge{
	int to, dis, next;
}edge[MAXN << 1];
void add_edge(int u, int v, int dis) {
	edge[++cnt].to = v;
	edge[cnt].dis = 0;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void init() {
	cnt = 1;
	memset(head, 0, sizeof(head));
	memset(depth, 0 , sizeof(depth));
	memset(fa, 0, sizeof(fa));
	ask.clear();
}
//更新每個節點的深度, 在搜索過程中 
void DFS (int now, int pre) {	 
	depth[now] = depth[pre] + 1;
	fa[now][0] = pre;
	for (int i = 1; (1 << i) <= depth[now]; ++i) {
		fa[now][i] = fa[fa[now][i - 1]][i - 1];
	}
	for(int i = head[now]; i; i = edge[i].next) {
		if(edge[i].to != pre) {
			DFS(edge[i].to, now);
		}
	}
}
int LCA(int a, int b) {
	//讓 a 處於更低的深度 
	if (depth[a] < depth[b]) swap(a, b);
	//然後讓 a 向上爬,爬到與b相同深度
	int dep = depth[a] - depth[b];
	for (int i = 0; (1 << i) <= dep; ++i) {
		if ((1 << i) & dep) {
			a = fa[a][i];
		}
	}
	//如果 b 爬到與 a 同一深度時 a == b 則直接返回該節點(該節點就是a b的LCA)  
	if(a == b) return a;
	//否者 a b 同時向上倍增  從最遠的開始試
	for (int i = log2(depth[a]); i >= 0; --i) {
			//如果父親不同就向上跳, 如果父親相同就減小距離再比較,直到不相同在跳。 
		if (fa[a][i] != fa[b][i]) {
			a = fa[a][i];
			b = fa[b][i];	
		}
	} 
	return fa[a][0];
	//跳到最後 a 和 b 距離最近的LCA只差一步 所以返回 dp[a][0] 即可 
}
int main() {
	scanf("%d%d%d", &N, &M, &S);
	init();
	int x, y;
	for (int i = 1; i < N; ++i) {
		scanf("%d%d", &x, &y);
		add_edge(x, y, 0);
		add_edge(y, x, 0);
	}	
	for (int i = 1; i <= M; ++i) {
		scanf("%d%d", &x, &y);
		ask.push_back(make_pair(x, y));
	}
	DFS(S, 0);
	for(int i = 0; i < ask.size(); ++i) {
		int ans = LCA(ask[i].first, ask[i].second);
		printf("%d\n", ans);
	}
	return 0;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章