完全二叉树求 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;
}