洛谷P3379 【模板】最近公共祖先(LCA)
题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式:第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:输出包含M行,每行包含一个正整数,依次为每一个询问的结果。
输入输出样例
输入样例#1:
5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5
输出样例#1:
4 4 1 4 4
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000
样例说明:
该树结构如下:
第一次询问:2、4的最近公共祖先,故为4。
第二次询问:3、2的最近公共祖先,故为4。
第三次询问:3、5的最近公共祖先,故为1。
第四次询问:1、2的最近公共祖先,故为4。
第五次询问:4、5的最近公共祖先,故为4。
故输出依次为4、4、1、4、4。
1 #include<iostream> 2 #include<cstdio> 3 4 using namespace std; 5 const int N=1000001; 6 7 struct node{ 8 int u,v,nxt; 9 }E[N]; 10 11 int head[N]; 12 int n,m,r; 13 int now=1; 14 int D[N]; 15 int f[N][20]; 16 17 inline void add(int u,int v) 18 { 19 E[now].u=u; 20 E[now].v=v; 21 E[now].nxt=head[u]; 22 head[u]=now++; 23 } 24 25 inline void read(int & x) 26 { 27 char c=getchar(); 28 x=0; 29 while(c<'0'||c>'9')c=getchar(); 30 while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); 31 } 32 33 inline void build_T(int r) 34 { 35 for(int i=head[r];i!=-1;i=E[i].nxt) 36 { 37 int v=E[i].v; 38 if(!D[v]) 39 { 40 D[v]=D[r]+1; 41 f[v][0]=r; 42 build_T(v); 43 } 44 } 45 } 46 47 inline void build_ST() 48 { 49 for(int i=1;i<=19;i++) 50 { 51 for(int j=1;j<=n;j++) 52 { 53 f[j][i]=f[f[j][i-1]][i-1]; 54 } 55 } 56 } 57 58 inline int LCA(int x,int y) 59 { 60 if(D[x]<D[y])swap(x,y); 61 for(int i=19;i>=0;i--) 62 { 63 if(D[f[x][i]]>=D[y]) 64 { 65 x=f[x][i]; 66 } 67 } 68 if(x==y)return y; 69 for(int i=19;i>=0;i--) 70 { 71 if(f[x][i]!=f[y][i]) 72 { 73 x=f[x][i]; 74 y=f[y][i]; 75 } 76 } 77 return f[x][0]; 78 } 79 80 int main() 81 { 82 read(n);read(m);read(r); 83 84 for(int i=1;i<=n;i++) 85 head[i]=-1; 86 for(int i=1;i<=n-1;i++) 87 { 88 int u,v; 89 read(u); 90 read(v); 91 add(u,v); 92 add(v,u); 93 } 94 D[r]=1; 95 build_T(r); 96 build_ST(); 97 98 for(int i=1;i<=m;i++) 99 { 100 int x,y; 101 read(x); 102 read(y); 103 printf("%d\n",LCA(x,y)); 104 } 105 106 }