1. 程式人生 > 實用技巧 >各種LCA——最近公共祖先

各種LCA——最近公共祖先

模板題目

倍增

#include<cmath> 
#include<cstdio>
#include<cstring>
const int N=500005;
int f[N<<1][20],dep[N],lg,ch,cnt,he[N],to[N<<1],ne[N<<1];
inline void rl(int &x){
	x=0;while((ch=getchar())<48||57<ch);
	for(;47<ch&&ch<58;ch=getchar())x=x*10+(ch^48);
}
#define add(u,v) {to[++cnt]=v;ne[cnt]=he[u];he[u]=cnt;}
typedef const int& ct;
inline int min(ct a,ct b){return a<b?a:b;}
void dfs(ct u,ct d){
	dep[u]=d;
	for(int j=1;j<=lg;++j)f[u][j]=f[f[u][j-1]][j-1];
	for(int i=he[u];i;i=ne[i])
	if(to[i]!=f[u][0])f[to[i]][0]=u,dfs(to[i],d+1);
}
inline int lca(int x,int y){
	if(int t=1&&dep[x]<dep[y])t=x,x=y,y=t;
	for(int i=lg;i>=0;--i)
		if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return y;
	for(int i=lg;i>=0;--i)
		if(f[x][i]!=f[y][i])
		x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main(){
	int n,m,s,x,y;
	rl(n);rl(m);rl(s);lg=(int)log2(n)+1;
	for(int i=1;i<n;++i){
		rl(x);rl(y);add(x,y);add(y,x);
	} 
	dfs(s,1);
	while(m--){
		rl(x);rl(y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

尤拉序+st表

#include<cmath> 
#include<cstdio>
#include<cstring>
const int N=500005;
int st[N<<1][22],el[N<<1],id[N<<1][20],ch,cnt,he[N],to[N<<1],ne[N<<1];//log2(2e5)<18
inline void rl(int &x){
	x=0;while((ch=getchar())<48||57<ch);
	for(;47<ch&&ch<58;ch=getchar())x=x*10+(ch^48);
}
#define add(u,v) {to[++cnt]=v;ne[cnt]=he[u];he[u]=cnt;}
typedef const int& ct;
inline int min(ct a,ct b){return a<b?a:b;}
void euler(ct u,ct dep){
	st[el[u]=++cnt][0]=dep;id[cnt][0]=u;
	for(int i=he[u];i;i=ne[i])if(!el[to[i]]){
		euler(to[i],dep+1);
		st[++cnt][0]=dep;id[cnt][0]=u;
	}
}
inline int lca(ct a,ct b){
	int x=el[a],y=el[b];if(x>y){int t=y;y=x;x=t;}int le=log2(y-x+1);
	//printf("x%d y%d le%d\n",x,y,le);
	return st[x][le]<st[y-(1<<le)+1][le]?
	id[x][le]:id[y-(1<<le)+1][le]; 
}
#define dbg1() {for(int i=0;i<n2;++i){printf("line%d:\t",i);for(int j=0;j<=8;++j)printf("%d ",id[i][j]);putchar(10);}}
#define dbg2() {for(int i=1;i<=n;++i)printf("%d:\t%d\n",i,el[i]);}
int main(){
	int n,m,s,x,y,lg,n2;
	rl(n);rl(m);rl(s);
	n2=n<<1;lg=20;
	for(int i=0;i<n2;++i)
	memset(st[i],63,lg+1<<2),memset(id[i],63,lg+1<<2);
	for(int i=1;i<n;++i){
		rl(x);rl(y);add(x,y);add(y,x);
	} 
	cnt=0;euler(s,1);
	//for(int i=1;i<n2;++i)printf("%d ",id[i][0]);putchar(10);
	for(int j=1,i,maxx;j<=lg;++j){
		maxx=n2-(1<<j);//間隔 -一個 
		for(i=1;i<=maxx;++i)
		if(st[i][j-1]<st[i+(1<<j-1)][j-1])
		st[i][j]=st[i][j-1],id[i][j]=id[i][j-1];
		else st[i][j]=st[i+(1<<j-1)][j-1],
		id[i][j]=id[i+(1<<j-1)][j-1];
	}
	//dbg1();dbg2();
	while(m--){
		rl(x);rl(y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

樹鏈剖分(第一次寫了14min,我太菜了)

#include<cstdio>
typedef const int ct;
ct N=1000006;
inline int rl(){
	int x=0,ch;while((ch=getchar())<48||57<ch);
	for(;47<ch&&ch<58;ch=getchar())x=x*10+(ch^48);
	return x;
}
int to[N],ne[N],he[N],size[N],fa[N],dep[N],son[N],id[N],cnt,top[N];
int dfs1(ct& u,ct& f){
	int size=1;
	for(int i=he[u],v,ss,ms=-1;i;i=ne[i])if((v=to[i])!=f){
		dep[v]=dep[u]+1;
		size+=ss=dfs1(v,fa[v]=u);
		if(ss>ms)son[u]=v,ms=ss;
	}
	return size;
}
void dfs2(ct& u,ct& topf){
	id[u]=++cnt;
	if(son[u])dfs2(son[u],top[son[u]]=topf);
	for(int i=he[u],v;i;i=ne[i])
	if((v=to[i])!=fa[u]&&v!=son[u])dfs2(v,top[v]=v);
}
inline int lca(int x,int y){
	int t;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])t=x,x=y,y=t;
		x=fa[top[x]];
	}
	//二者同一重鏈 
	return dep[x]<dep[y]?x:y;
}
int main(){
	int n=rl(),m=rl(),s=rl();
	for(int i=1,x,y,cnt=0;i<n;++i){
		to[++cnt]=y=rl();ne[cnt]=he[x=rl()];he[x]=cnt;
		to[++cnt]=x;ne[cnt]=he[y];he[y]=cnt;
	}
	dep[s]=1;
	dfs1(s,fa[s]=-1);
	dfs2(s,top[s]=s);
	while(m--)printf("%d\n",lca(rl(),rl()));
	return 0;
}