倍增lca
预处理每个节点的深度,从节点(u)向上跳(2^k(0leq kleq log n))步所能到达的节点编号
预处理时间复杂度(O(nlog n))
查询时间复杂度(O(log n))
const int maxn=40010;
int depth[maxn],fa[maxn][20];
void dfs(int u,int f){
fa[u][0]=f;
for(int i=head[u];~i;i=ne[i]){
int v=to[i];
if(v!=f){
depth[v]=depth[u]+1;
dfs(v,u);
}
}
}
void lca_init(){
depth[1]=1;
dfs(1,0);
for(int j=1;j<20;j++){
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
}
int lca(int x,int y){
if(depth[x]>depth[y]) swap(x,y);
int d=depth[y]-depth[x];
for(int i=0;i<20;i++){
if((1<<i)&d) y=fa[y][i];
}
if(x==y) return x;
for(int i=19;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
模板题:hdu2586 How far away ?
基于rmq的算法
按照从根节点开始的(dfs)访问顺序得到顶点序列(vs[maxn]),节点深度(depth[maxn]),每个节点第一次出现在顶点序列中的下标(id[maxn])
设(id[u]leq id[v]),则有:
[lca(u,v)=vs[id[u]leq ileq id[v]中令depth[i]最小的i]
]
可以通过(rmq)查询,返回的是下标而不是最值
const int maxn=10010,maxm=20010;
int n,st[2*maxn][15],mini[2*maxn],idx[2*maxn][15];
int vs[2*maxn],depth[2*maxn],id[maxn];
void rmq_init(int n){
for(int i=1;i<=n;i++){
st[i][0]=depth[i];
idx[i][0]=i;
}
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
if(st[i][j-1]<st[i+(1<<(j-1))][j-1]){
st[i][j]=st[i][j-1];
idx[i][j]=idx[i][j-1];
}
else{
st[i][j]=st[i+(1<<(j-1))][j-1];
idx[i][j]=idx[i+(1<<(j-1))][j-1];
}
}
}
for(int len=1;len<=n;len++){
int k=0;
while((1<<(k+1))<=len) k++;
mini[len]=k;
}
}
int rmq_id(int l,int r){
int k=mini[r-l+1];
if(st[l][k]<st[r-(1<<k)+1][k]) return idx[l][k];
return idx[r-(1<<k)+1][k];
}
void dfs(int u,int fa,int dep,int& k){
id[u]=k;
vs[k]=u;
depth[k++]=dep;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
dfs(v,u,dep+1,k);
vs[k]=u;
depth[k++]=dep;
}
}
void lca_init(int root){
int k=1;
dfs(root,-1,0,k);
rmq_init(2*n-1);
}
int lca(int u,int v){
return vs[rmq_id(min(id[u],id[v]),max(id[u],id[v]))];
}