【题目来源】
https://www.luogu.com.cn/problem/P3379
【题目描述】
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
【输入格式】
第一行包含三个正整数 N,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来 N−1 行每行包含两个正整数 x,y,表示 x 结点和 y 结点之间有一条直接连接的边(数据保证可以构成树)。
接下来 M 行每行包含两个正整数 a,b,表示询问 a 结点和 b 结点的最近公共祖先。
【输出格式】
输出包含 M 行,每行包含一个正整数,依次为每一个询问的结果。
【输入样例】
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
【输出样例】
4
4
1
4
4
【说明/提示】
对于 30% 的数据,N≤10,M≤10。
对于 70% 的数据,N≤10000,M≤10000。
对于 100% 的数据,1≤N,M≤500000,1≤x,y,a,b≤N,不保证 a≠b。
【算法分析】
● 一般来讲,求 LCA 有 3 种常见方法:倍增、RMQ+欧拉序、Tarjan(离线)。本题代码介绍“Tarjan”法求LCA。
● Tarjan算法求 LCA 是离线算法,基于后序 DFS(深度优先搜索)和并查集。
● 并查集:https://blog.csdn.net/hnjzsyjyj/article/details/120147618
int find(int x) {
if(x!=pre[x]) pre[x]=find(pre[x]);
return pre[x];
}
void merge(int x,int y) {
int a=find(x);
int b=find(y);
if(a!=b) pre[a]=b;
}
● 快读:https://blog.csdn.net/hnjzsyjyj/article/details/120131534
int read() { //fast read
int x=0,f=1;
char c=getchar();
while(c<'0' || c>'9') { //!isdigit(c)
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9') { //isdigit(c)
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
● 链式前向星:https://blog.csdn.net/hnjzsyjyj/article/details/139369904
val[idx]:存储编号为 idx 的边的值
e[idx]:存储编号为 idx 的结点的值
ne[idx]:存储编号为 idx 的结点指向的结点的编号
h[a]:存储头结点 a 指向的结点的编号
● 结构体构造函数:https://blog.csdn.net/hnjzsyjyj/article/details/139553390
#include <bits/stdc++.h>
using namespace std;
struct Point {
int x,y;
Point() {} //无参构造
Point(int _x,int _y):x(_x),y(_y) {} //有参构造
} pt[10];
int main() {
int cnt=0;
for(int i=1; i<=3; i++) {
for(int j=1; j<=3; j++) {
pt[cnt++]=Point(i,j);
}
}
for(int i=0; i<cnt; i++) {
printf("%d %d\n",pt[i].x,pt[i].y);
}
return 0;
}
/*
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
*/
【算法代码】
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,m,root;
int u,v;
int e[N<<1],ne[N<<1],h[N],idx=1;
int pre[N],ans[N],st[N];
struct node {
int val,id;
node(int _val,int _id):val(_val),id(_id) {} //结构体构造函数
};
vector<node> q[N];
inline int read() { //fast read
int x=0,f=1;
char c=getchar();
while(c<'0' || c>'9') { //!isdigit(c)
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9') { //isdigit(c)
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
inline void add(int a,int b) {
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
inline int find(int x) {
if(x!=pre[x]) pre[x]=find(pre[x]);
return pre[x];
}
inline void merge(int x,int y) {
int a=find(x);
int b=find(y);
if(a!=b) pre[a]=b;
}
void dfs(int u, int fa) {
for(int i=h[u]; ~i; i=ne[i]) {
int j=e[i];
if(j!=fa) {
dfs(j,u);
pre[j]=u;
}
int len=q[u].size();
for(int i=0; i<len; i++) {
if(st[q[u][i].val]) ans[q[u][i].id]=find(q[u][i].val);
}
st[u]=1;
}
}
int main() {
memset(h,-1,sizeof(h));
n=read(),m=read(),root=read();
for(int i=1; i<n; i++) {
u=read(),v=read();
add(u,v),add(v,u);
}
for(int i=1; i<=m; i++) {
u=read(),v=read();
q[u].push_back(node(v,i));
q[v].push_back(node(u,i));
}
for(int i=1; i<=n; i++) pre[i]=i;
dfs(root,0);
for(int i=1; i<=m; i++) printf("%d\n",ans[i]);
return 0;
}
/*
in:
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
out:
4
4
1
4
4
*/
【参考文献】
https://blog.csdn.net/JKdd123456/article/details/81388495
https://maimai.cn/article/detail?fid=459603071&efid=m4kdxUnQtYF4sqHKVd_KtA
https://www.cnblogs.com/fu3638/p/8639429.html
https://zhuanlan.zhihu.com/p/627024371