O(1)LCA

大家常用的三种LCA算法如下:
倍增为在线,复杂度\(\Theta(n\log n)\)预处理,\(\Theta(\log n)\)查询。
树剖为在线,复杂度\(\Theta(n)\)预处理,\(\Theta(\log n)\)查询。
Tarjan为离线,\(\Theta(n+q)\)复杂度。

现在介绍\(\Theta(n\log n)\)时间预处理,\(\Theta(1)\)查询的在线LCA算法。
欧拉序:
其实就是前序遍历时每次回溯也加入序列当中。
[pkwAQ6P.png]
如图的欧拉序为
1 2 4 8 4 7 4 2 5 2 1 3 9 3 10 12 10 3 11 3 1
记录每个点第一次在欧拉序中出现的位置\(p_i\),如求LCA(5,10)
\(p_5\)\(p_{10}\)之间的序列为
5 2 1 3 9 3 10
其中LCA(5,10)=1的深度为要求序列中最小的。
其他的点也类似,于是可以用ST表来维护区间深度最小值。
欧拉序的长度为\(\Theta(n)\)的,于是预处理复杂度\(\Theta(n\log n)\),查询复杂度\(\Theta(1)\)
代码如下

const int N=500005;
int n,head[N],pedge,dfn[N],lg[N<<1],cnt,id[N<<1],dep[N],st[N<<1][20];
struct Edge{
	int to,next;
}edge[N<<1];
void ins(int u,int v){
	edge[++pedge]={v,head[u]},head[u]=pedge;
}
void dfs(int u,int father){
	dfn[u]=++cnt,id[cnt]=u,dep[u]=dep[father]+1;
	for(int e=head[u];e;e=edge[e].next)if(edge[e].to^father)dfs(edge[e].to,u),id[++cnt]=u;
}
void init(){//预处理
    dfs(s,0);
	lg[0]=-1;
	for(int i=1;i<=cnt;i++)st[i][0]=id[i],lg[i]=lg[i>>1]+1;
	for(int j=1;j<=19;j++)for(int i=1;cnt-i+1>=1<<j;i++){
		int f1=st[i][j-1],f2=st[i+(1<<j-1)][j-1];
		st[i][j]=dep[f1]<dep[f2]?f1:f2;
	}
}
int lca(int u,int v){//求lca
	u=dfn[u],v=dfn[v];
	if(u>v)u^=v^=u^=v;
	int k=lg[v-u+1],f1=st[u][k],f2=st[v-(1<<k)+1][k];
	return dep[f1]<dep[f2]?f1:f2;
}
posted @ 2024-06-15 16:53  junjunccc  阅读(46)  评论(2编辑  收藏  举报