[OI学习笔记]倍增LCA
这也是洛谷P3379 【模板】最近公共祖先(LCA)的代码
1.首先预处理upto[i][j]表示点i向上跳2j 个点到达的点,d[i]表示i的深度
2.然后把两个被询问的点搬到同一深度,具体操作是:
假设深的点为a,那么a每次把a迭代为 upto[a][log2(d[a]-d[b])] ,直到d[a]==d[b]
3.将他们不断向上跳尽量大,使他们不重合,直到不能再在这个条件下向上跳
具体操作是从大到小枚举k(最大 k=log2(深度) ,因为最多跳到root),然后a,b都向上跳2k 个点,直到不能再跳
4.不能跳之后,那么ans就是他们的父亲了
(p.s. 还可以实现预处理数组lg[i]=log2(i)+1以加快速度,具体实现看代码)
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> using namespace std; const int MAXN = 500001; inline int read(){ int x=0,w=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')w=-1; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } return x*w; } int n,t,root; int upto[MAXN][25],d[MAXN]={0},first[MAXN],lg[MAXN]; struct edge{ int u,v,next; }e[2*MAXN]; int tot=0; void insert(int u,int v){ ++tot;e[tot].u=u;e[tot].v=v;e[tot].next=first[u];first[u]=tot; } void init(int u,int fa){ d[u]=d[fa]+1; upto[u][0]=fa; for(int k=1;(1<<k)<=d[u];k++){ upto[u][k]=upto[upto[u][k-1]][k-1]; } for(int i=first[u];i!=-1;i=e[i].next){ int v=e[i].v; if(v!=fa)init(v,u); } } int solve(int a,int b){ if(d[a]<d[b])swap(a,b); while(d[a]>d[b]){ a=upto[a][lg[d[a]-d[b]]-1]; } if(a==b)return a; for(int k=lg[d[a]]-1;k>=0;k--) if(upto[a][k]!=upto[b][k]){ a=upto[a][k];b=upto[b][k]; } return upto[a][0]; } int main(){ memset(first,-1,sizeof(first)); n=read();t=read();root=read(); for(int i=1;i<n;i++){ int x=read(),y=read(); insert(x,y); insert(y,x); } init(root,0); for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);//预处理lg[i]=log2(i)+1 for(int i=1;i<=t;i++){ int a=read(),b=read(); printf("%d\n",solve(a,b)); } return 0; }
本篇文章为SHINE_GEEK原创,转载请注明来源!
-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------
written_by:SHINE_GEEK
blog_addr:www.cnblogs.com/sjrb
-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------