完全二叉树求 lca
学习自 https://blog.csdn.net/kksleric/article/details/7836649
先考虑完全二叉树的 lca 求法。中序遍历分配编号。

设第 位是 最左边的 ,则 是 的 位以左、第 位是 , 位以右是 。
将树上 lca 转到完全二叉树上。
先序遍历,设 表示 的末尾连续 数, 表示 子树内最大 的值, 表示 对应的 。相等的 形成若干条链。有若 是 的祖先, 是 完全二叉树中的祖先。

设 。则 是 的公共祖先。树上根到节点路径上 单调不降,但在树上的 值不连续。设 表示从根到 哪些 值存在。可以求出 和 在完全二叉树上的 lca 的末尾零数 ,在 和 中都出现的最小的 就是 的末尾零数。
每种 值都会形成一条链。找到 到链 上的最近祖先深度较小的一个即为 lca。可以找到 中小于 的最大的 ,这条链的链顶的父亲在链 上。
预处理 ,查询 ,空间 。理论上完全偏序所有 lca 算法。
int idx,dfn[maxn],rnk[maxn],l[maxn],fa[maxn],dep[maxn];
void dfs(int u){
dep[u]=dep[fa[u]]+1;
rnk[dfn[u]=++idx]=u;l[u]=__builtin_ctz(idx);
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa[u])continue;
fa[v]=u;dfs(v);
l[u]=max(l[u],l[v]);
}
}
int tp[maxn];unsigned int a[maxn];
void dfs1(int u,int lst){
a[u]=a[fa[u]]|(1<<l[u]);tp[u]=lst;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa[u])continue;
if(l[u]==l[v])dfs1(v,lst);
else dfs1(v,v);
}
}
int lca(int u,int v){
if(u==v)return u;
int d=31^__builtin_clz(dfn[u]^dfn[v]);
int s=__builtin_ctz(((a[u]&a[v])>>d)<<d);
int du=31^__builtin_clz(a[u]<<(32-s)>>(32-s)),uu=(l[u]!=s?fa[tp[rnk[((dfn[u]>>du)|1)<<du]]]:u);
int dv=31^__builtin_clz(a[v]<<(32-s)>>(32-s)),vv=(l[v]!=s?fa[tp[rnk[((dfn[v]>>dv)|1)<<dv]]]:v);
if(dep[uu]<dep[vv])return uu;
return vv;
}
dfn 序 ST 表
的 dfn 序到 的 dfn 序之间必然不存在存在 和 的祖先,必然存在 的儿子。 的 dfn 序到 的 dfn 序之间深度最小的点的父亲为 。
预处理 ,查询 ,空间 。
int dfn[maxn],idx;
int st[20][maxn];
void dfs(int u,int fa){
st[0][dfn[u]=++idx]=fa;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa)dfs(v,u);
}
}
int lca(int u,int v){
if(u==v)return u;
u=dfn[u],v=dfn[v];
if(u>v)swap(u,v);u++;
int k=log2(v-u+1);
if(dfn[st[k][u]]<dfn[st[k][v-(1<<k)+1]])return st[k][u];
return st[k][v-(1<<k)+1];
}
void init(){
for(int j=1;j<20;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
if(dfn[st[j-1][i]]<dfn[st[j-1][i+(1<<j-1)]])st[j][i]=st[j-1][i];
else st[j][i]=st[j-1][i+(1<<j-1)];
}
}
}
倍增
预处理 ,查询 ,空间 ,常数大。
int dep[maxn],f[maxn][21];
void dfs(int u,int fa){
dep[u]=dep[fa]+1;f[u][0]=fa;
for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa)continue;
dfs(v,u);
}
}
int lca(int x,int y){
if(dep[x]>dep[y])swap(x,y);
for(int i=20;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];
if(x==y)return x;
for(int i=20;i>=0;i--)if(f[y][i]!=f[x][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
树剖
预处理 ,查询 ,极难卡满,空间 。
int dep[maxn],fa[maxn],siz[maxn],son[maxn];
void dfs(int u){
dep[u]=dep[fa[u]]+1;siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa[u]){
fa[v]=u;
dfs(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
}
int dfn[maxn],idx,rnk[maxn],tp[maxn];
void dfs2(int u,int lst){
rnk[dfn[u]=++idx;]=u;tp[u]=lst;
if(!son[u])return ;
dfs2(son[u],lst);
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa[u]&&v!=son[u])dfs2(v,v);
}
}
int lca(int u,int v){
while(tp[u]!=tp[v]){
if(dep[tp[u]]<dep[tp[v]])swap(u,v);
u=fa[tp[u]];
}
if(dep[u]>dep[v])swap(u,v);
return u;
}