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

查看原题: P3379 【模板】最近公共祖先(LCA)
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

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

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

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

输出格式 输出包含 MM 行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例 : 输入 #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 说明/提示 对于 30%30% 的数据,N\leq 10N≤10,M\leq 10M≤10。

对于 70%70% 的数据,N\leq 10000N≤10000,M\leq 10000M≤10000。

对于 100%100% 的数据,N\leq 500000N≤500000,M\leq 500000M≤500000。

题解:

  1. LCA 题解:(只是敲一个板子, 时间超限)
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 20;
bool vis[N];
int dep[N];
vector<int >g[N];
int n, m, s;
void dfs(int u){
	int sz = g[u].size();
	for(int i = 0; i < sz; i ++){
		int v = g[u][i];
		if(v == p[u])continue;
		p[v] = u;
		dfs(u);
	}
}

int LCA(int x, int y){
	memset(vis, 0, sizeof(vis));
	while(x){
		vis[x] = true;
		x = p[x];
	}
	while(!vis[y]) y = p[y];
	return y;
}
int main(){
	cin >> n >> m >> s;
	for(int i = 1; i <= n; i ++){
		int x, y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y],push_back(x);
	}
	dfs(s);
	while(m --){
		int x, y;
		cin >> x >> y;
		cout << LCA(x, y)
	}
}

2.改进后, LCA+ST, 减少时间:
(1)更换了一下邻接表,可换可不换;
(2)增加ST表模板, 这里要计算深度:
思路:

邻接表存储

			struct node{
				int v, next;
			}tree[N<<1];

            void add(int x, int y){
				tree[++ cnt].v = y;
				tree[cnt].next = head[x];
				head[x] = cnt;
				return;
			}

dfs遍历树, 得到dep[], f[][];

           void dfs(int fa, int x){
				vis[x] = 1;
				depth[x] = depth[fa] + 1;
				f[x][0] = fa;
				for(int i = 1; i < 20; i ++){
					f[x][i] = f[f[x][i-1]][i-1];
				}
				for(int i = head[x]; i ; i = tree[i].next){
					if(!vis[tree[i].v]){
						dfs(x, tree[i].v);
					}
				}
				vis[x] = 0;
				return ;
			}

LCA算法求解:

int LCA(int x, int y){
	// 保证x的深度不小于y
	if(depth[x] < depth[y]){
		swap(x, y);
	} 
	
	int z = 1 << 19;
	for(int i = 19; i >= 0; i--){
		if(depth[x]-z >= depth[y]){
			 x = f[x][i];
		}	    
        z >>= 1;
	} 
	if(x == y){
		return x;
	}
	// 同一深度的 
	for(int i = 19; i >= 0; i --){
		if(f[x][i]!=f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}
#include<bits/stdc++.h>
using namespace std;
const int N = 5*1e5 + 20;
int vis[N];
int f[N][20];
int depth[N];
int head[N];
struct node{
	int v, next;
}tree[N<<1];

int cnt = 0;
void add(int x, int y){
	tree[++ cnt].v = y;
	tree[cnt].next = head[x];
	head[x] = cnt;
	return;
}

void dfs(int fa, int x){
	vis[x] = 1;
	depth[x] = depth[fa] + 1;
	f[x][0] = fa;
	for(int i = 1; i < 20; i ++){
		f[x][i] = f[f[x][i-1]][i-1];
	}
	for(int i = head[x]; i ; i = tree[i].next){
		if(!vis[tree[i].v]){
			dfs(x, tree[i].v);
		}
	}
	vis[x] = 0;
	return ;
}

int LCA(int x, int y){
	// 保证x的深度不小于y
	if(depth[x] < depth[y]){
		swap(x, y);
	} 
	
	int z = 1 << 19;
	for(int i = 19; i >= 0; i--){
		if(depth[x]-z >= depth[y]){
			 x = f[x][i];
		}	    
        z >>= 1;
	} 
	if(x == y){
		return x;
	}
	// 同一深度的 
	for(int i = 19; i >= 0; i --){
		if(f[x][i]!=f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}
int main(){
	int n, m, s;
	cin >> n >> m >> s;
	for(int i = 1; i < n; i ++){
		int x, y;
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	dfs(0, s);
	for(int i = 1; i <= m; i ++){
		int x, y;
		cin >> x >> y;
		cout << LCA(x, y) << endl;
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值