查看原题: 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。
题解:
- 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;
}