[Graph]Doubling Algorithm
Doubling Algorithm——倍增算法
是一种可以优化时间复杂度的神奇的算法,可以用它来求LCA等等。
基本思想:
deep[i] 表示 i节点的深度, fa[i,j]表示 i 的 2^j (即2的j次方) 倍祖先,那么fa[i , 0]即为节点i 的父亲,
然后就有一个递推式子:fa[i,j]= fa [ fa [i,j-1] , j-1] 。
那么怎么求LCA呢,我们需先把深度更深的节点往上提直到深度和另一个节点一样,然后我们每次调2^i个节点(从最大可以
跳的步数开始跳,若相反可能会导致找到的节点不是最近的),逐步缩小范围。
Luogu 3379:
#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<iostream> #define maxn 1000005 #define maxm 500005 using namespace std; int n,p,gen; struct Tree{ int next,to; }tree[maxn]; int head[maxm]={0},f[maxm][21]={0},deep[maxm]={0},tot=0; void add(int x,int y){ tot++; tree[tot].to=y; tree[tot].next=head[x]; head[x]=tot; } void dfs(int u,int fa){ deep[u]=deep[fa]+1; //printf("u:%d deep:%d\n",u,deep[u]); f[u][0]=fa; int i; for( i=1;(1<<i)<=deep[u];i++){ //if(f[u][i-1]) f[u][i]=f[f[u][i-1]][i-1]; //else break; } for(i=head[u];i!=-1;i=tree[i].next){ if(tree[i].to!=fa) dfs(tree[i].to,u); } } int lca(int x,int y){ if(deep[x]<deep[y]) swap(x,y); for(int i=20;i>=0;i--){ if(deep[y]<=deep[x]-(1<<i)) x=f[x][i]; } if(x==y) return x; for(int i=20;i>=0;i--){ if(f[x][i]!=f[y][i]){ x=f[x][i]; y=f[y][i]; } } // printf("x:%dy:%d\n",x,y); return f[y][0]; } int main() { memset(head,-1,sizeof(head)); int x,y,i; scanf("%d%d%d",&n,&p,&gen); for(i=1;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(gen,0); for(i=1;i<=p;i++){ scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; }