LCA
LCA
主要思想:倍增
首先预处理一一个数组\(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\)的父节点编号。
一个重要的“状态转移”式:
“爸爸的爸爸是什么?”
在\(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$的子节点的位置为止。 ```cpp 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: ```cpp #include
using namespace std;
const int N=500005;
int n,m,FA[N][50],s,DEP[N],lg[N];
vector
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;
}
本文来自博客园,作者:miyasaka,转载请注明原文链接:https://www.cnblogs.com/kion/p/11817153.html