嗯...今天只更新倍增版的 明天看写不写st表 树剖 tarjan等其他实现方式
1.写在前面
其实倍增版lca很好理解 代码看着也不恶心 但据说常数大 跑得慢(相比其他的几种方式)
2.细节一----关于邻接表的处理
无向图记得add两次就好了.....以及edge数组大小——边的两倍
细节二----fa[now][i]=fa[fa[now][i-1]][i-1];
这个比较重要由于不是二叉树 因此无法直接*2
fa[now][i-1]是now往上跳2^(i-1)的位置
由于2^i=2^(i-1)+2^(i-1) 因此再跳2^(i-1)就行了
(fa[now][i-1]之前必定处理过,fa[fa[now][i-1]][i-1]也是)
然后应该没啥了..其他细节应该没什么了 看看我的注释就好了
#include<bits/stdc++.h>
using namespace std;
int n,m,s,tot;
int first[500005],deepth[500005],fa[500005][25];
struct node
{
int to;
int next;
}edge[500005*2];
int read()
{
int k=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch))
{
k=k*10+ch-'0';
ch=getchar();
}
return k;
}
inline void add(int x,int y)
{
++tot;
edge[tot].to=y;
edge[tot].next=first[x];
first[x]=tot;
}
void dfs(int now,int father)
{
deepth[now]=deepth[father]+1;
fa[now][0]=father; //2的0次方是1 距离1当然就是自己的爸爸
for(int i=1;(1<<i)<=deepth[now];i++) //找now往上的fa数组
{
fa[now][i]=fa[fa[now][i-1]][i-1]; //不是二叉树 因此无法直接*2 //fa[now][i-1]是now往上跳2^i-1的位置
// 由于2^i=2^(i-1)+2^(i-1) 因此再跳2^(i-1)就行了
}
for(int i=first[now];i;i=edge[i].next)
{
int vis=edge[i].to;
if(vis!=father) dfs(vis,now);
}
}
int lca(int x,int y)
{
if(deepth[x]<deepth[y]) swap(x,y); //保证x的深度总是大的那个
for(int i=21;i>=0;--i)
{
if(deepth[fa[x][i]]>=deepth[y]) x=fa[x][i]; //x往上跳 直到跳到一个点 使得这个点的深度=y的深度(y)(由于2^i相加可以到达任何点)
}
if(x==y) return x;
for(int i=21;i>=0;--i)
{
//两个节点最近公共祖先以上的根必定也是这两个点的祖先
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
//使x,y成为最浅的满足deepth[x]==deepth[y],且x!=y的两个点
//此时的x,y 不管i为多少 fa[x][i]一定等于fa[y][i]
}
return fa[x][0];
}
int main()
{
// freopen("wordin.in","r",stdin);
n=read();
m=read();
s=read();
for(int i=1;i<=n-1;i++)
{
int x,y;
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(s,0); //默认起点的根为0
for(int i=1;i<=m;i++)
{
int x,y;
x=read();
y=read();
printf("%d\n",lca(x,y));
}
return 0;
}