LCA回顾(主要针对倍增算法)
这个有两种方法:
一、倍增
就是维护一个节点的第\(2^0,\ 2^1,\ 2^2\ ...\)层父亲,这样的话我们在后面查询的时候就可以直接“跳”着找。
void pushup()
{
step=log(maxd*1.0)/log(2.0);
for(int i=1;i<=step;i++)
for(int p=1;p<=n;p++) if( fa[p][i-1] ) fa[p][i]=fa[ fa[p][i-1] ][i-1];
return;
}
然后查询的时候,先让两个点跳到同一深度。
所以要让较深的一个点先往上跳:
if( dep[x]<dep[y] ) swap(x,y);
for(int i=step;i>=0;i--) if( dep[x]-(1<<i)>=dep[y] ) x=fa[x][i];
第二行相当于将\(dep(x)-dep(y)\)的值做二进制拆分。
跳到同一深度之后,如果两个节点已经跳到同一个位置,就直接退出。
否则,就要继续同时往上跳,但是,在跳的时候要保证:它们各自跳到的位置必须是不一样的!不然的话它们可能都跳到了\(LCA\)的上面!
整个功能段见下(省略了建图部分):
struct LCA
{
int fa[maxn][22],dep[maxn];
int maxd;
void dfs(int x,int f,int d)
{
fa[x][0]=f,dep[x]=d;
maxd=max( maxd,d );
for(int i=G.Head[x];i;i=G.Next[i])
{
Graph::Edge e=G.edges[i];
if( e.to==f ) continue;
dfs(e.to,x,d+1);
}
return;
}
int step;
void pushup()
{
step=log(maxd*1.0)/log(2.0);
for(int i=1;i<=step;i++)
for(int p=1;p<=n;p++) if( fa[p][i-1] ) fa[p][i]=fa[ fa[p][i-1] ][i-1];
return;
}
int LCA(int x,int y)
{
if( dep[x]<dep[y] ) swap(x,y);
for(int i=step;i>=0;i--) if( dep[x]-(1<<i)>=dep[y] ) x=fa[x][i];
if( x==y ) return y;
for(int i=step;i>=0;i--)
if( fa[x][i]!=fa[y][i] ) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
}SOL;
二、树链剖分
这个更简单,用\(top\)数组直接跳就行了……