LCA Template
主要思想:倍增
首先预处理一一个数组\(lg[]\),\(lg[x]=2^x+1\)。
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(i==(1<<lg[i-1]));
再预处理一个数组\(FA[][]\),\(FA[u][x]\)是\(u\)的\(2^x\)级的祖先。例如,\(FA[u][0]\)是\(u\)的父节点编号。
一个重要的“状态转移”式:
\[ FA[u][i]=FA[FA[u][i-1]][i-1]; \]
“爸爸的爸爸是什么?”
在\(dfs\)中转移即可。
\(DEP[u]\)表示u节点的深度,指数的参量小于\(\log_2 DEP[u]\)就够了。
void dfs(int u,int fa)
{
DEP[u]=DEP[fa]+1;
FA[u][0]=fa;
for(int i=1;i<lg[DEP[u]];i++)
FA[u][i]=FA[FA[u][i-1]][i-1];
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v!=fa) dfs(v,u);
}
}
那么预处理就完成了,接下来要开始跳了;
对于一个询问:\(x\),\(y\),我们想要只要这两个人的最近公共祖先。
“不妨令\(DEP[x] \geq DEP[y]\)”:
if(DEP[x]<DEP[y])swap(x,y);
让\(x\)往与\(y\)相同深度的位置跳跃,注意不要超过根节点。这里的原理是\(DEP[x]-2^{lg[DEP[x]-DEP[y]]-1} \geq DEP[y]\)是恒成立的。
while(DEP[x]!=DEP[y])x=FA[x][ lg[DEP[x]-DEP[y]]-1];
好的,现在\(x,y\)都在同一个深度了。如果两人现在是同一个东西,返回;
否则\(x,y\)一起往上跳跃,直到跳到他们\(LCA\)的子节点的位置为止。
for(int k=lg[DEP[x]]-1;k>=0;k--)
{
if(FA[x][k]!=FA[y][k])
{
x=FA[x][k];
y=FA[y][k];
}
}
code:
#include <bits/stdc++.h>
using namespace std;
const int N=500005;
int n,m,FA[N][50],s,DEP[N],lg[N];
vector<int>G[N];
inline int read()
{
char c=getchar();int x=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
x=x*10+c-'0';
return x;
}
void dfs(int u,int fa)
{
// cout<<"DFS"<<endl;
DEP[u]=DEP[fa]+1;
FA[u][0]=fa;
for(int i=1;i<lg[DEP[u]];i++)
{
FA[u][i]=FA[FA[u][i-1]][i-1];
}
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v!=fa)
{
dfs(v,u);
}
}
}
inline int LCA(int x,int y)
{
if(DEP[x]<DEP[y])swap(x,y);
while(DEP[x]!=DEP[y])x=FA[x][ lg[DEP[x]-DEP[y]]-1];
if(x==y)return x;
for(int k=lg[DEP[x]]-1;k>=0;k--)
{
if(FA[x][k]!=FA[y][k])
{
x=FA[x][k];
y=FA[y][k];
}
}
return FA[x][0];
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(i==(1<<lg[i-1]));
for(int i=1;i<=n-1;i++)
{
int u=read();
int v=read();
G[u].push_back(v);
G[v].push_back(u);
}
dfs(s,0);
for(int i=1;i<=m;i++)
{
int u=read();
int v=read();
printf("%d\n",LCA(u,v));
}
return 0;
}