LCA(樹上倍增)
阿新 • • 發佈:2019-01-24
LCA樹上倍增和RMQ差不多都是離線演算法,大家可以上b戰看看那個每週演算法講壇。
#include<iostream> #include<cmath> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<ctime> using namespace std; const int maxn=250020; int n,q; int g[100][100],ne,root; int p[maxn][20];//p[i][j]表示i結點的第2^j祖先 int deep[maxn];//i的深度 int use[1000][1000]; void build(int x)//處理樹深度 { for(int i=1;i<=n;i++) if(g[x][i]==1) { deep[i]=deep[x]+1; p[i][0]=x; g[x][i]=g[i][x]=0; build(i); } } void pre()//處理祖先 { int i,j; for(j=1;(1<<j)<=n;j++) for(i=1;i<=n;i++)//! !j在i前面!!記住 if(p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先 } int lca(int a,int b)//最近公共祖先 { //使a,b兩點的深度相同 int i,j; if(deep[a]<deep[b])swap(a,b); for(i=0;(1<<i)<=deep[a];i++); i--; for(j=i;j>=0;j--) if(deep[a]-(1<<j)>=deep[b]) a=p[a][j]; if(a==b)return a; //倍增法,每次向上進深度2^j,找到最近公共祖先的子結點 for(j=i;j>=0;j--) { if(p[a][j]!=-1&&p[a][j]!=p[b][j])//!! { a=p[a][j]; b=p[b][j]; } } return p[a][0];//即正好竄到兩個子樹上,最近祖先為它的j=0的祖先 } int main() { cin>>n>>q; for(int i=1;i<=n;i++) { int a,b; cin>>a>>b; g[a][b]=g[b][a]=1; } p[1][0]=-1; deep[1]=1; build(1); pre(); for(int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; }