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\)的父节点编号。
一个重要的“状态转移”式:

\[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$的子节点的位置为止。 ```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];
vectorG[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;
}

posted @ 2019-11-07 23:56  miyasaka  阅读(147)  评论(0)    收藏  举报