倍增
#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;
}