题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式:第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:输出包含M行,每行包含一个正整数,依次为每一个询问的结果。
输入输出样例
说明
时空限制: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。
题解
其实是放一下代码
众所周知,LCA有几种常见的做法
- 暴力跳
- 先把较深的往上跳,跳到同一深度
- 然后一起跳
- 单次复杂度$O(n)$分分钟带你上天
- 倍增
- 在跳的时候优化一下,不一格一格的跳,而是拆分成二进制跳
- 需要预处理出每个点往上$2^i$步的祖先,
- 时间复杂度$O(nlogn+mlogn)$,空间复杂度$O(nlogn)$
-
1 /* 2 qwerta 3 P3379 【模板】最近公共祖先(LCA) 4 Accepted 5 100 6 代码 C++,1.37KB 7 提交时间 2018-03-13 18:33:35 8 耗时/内存 9 1672ms, 51789KB 10 */ 11 #include<iostream> 12 #include<cstdio> 13 #include<algorithm> 14 using namespace std; 15 struct emm{ 16 int f,e; 17 }a[1000007]; 18 int h[500007]; 19 int d[500007]; 20 int p[500007][20]; 21 void dfs(int no,int fa) 22 { 23 d[no]=d[fa]+1; 24 //cout<<no<<" "<<d[no]<<" "<<fa<<endl; 25 p[no][0]=fa; 26 int w; 27 for(w=1;w<20;w++) 28 p[no][w]=p[p[no][w-1]][w-1]; 29 for(w=h[no];w;w=a[w].f) 30 { 31 if(a[w].e!=fa) 32 dfs(a[w].e,no); 33 } 34 return; 35 } 36 int main() 37 { 38 int c=0,x,y,n,m,s,i,j; 39 scanf("%d%d%d",&n,&m,&s); 40 for(i=1;i<n;++i) 41 { 42 scanf("%d%d",&x,&y); 43 ++c; 44 a[c].f=h[x]; 45 h[x]=c; 46 a[c].e=y; 47 ++c; 48 a[c].f=h[y]; 49 h[y]=c; 50 a[c].e=x; 51 d[i]=99999999; 52 } 53 d[n]=99999999; 54 dfs(s,0); 55 for(i=1;i<=m;++i) 56 { 57 scanf("%d%d",&x,&y); 58 if(d[x]<d[y])swap(x,y); 59 for(j=19;j>=0;--j) 60 { 61 if((d[x]-d[y])>=(1<<j)) 62 { 63 x=p[x][j]; 64 //cout<<x<<" "; 65 } 66 } 67 if(x==y)printf("%d\n",x); 68 else{ 69 for(j=19;j>=0;--j) 70 { 71 if(p[x][j]!=p[y][j]) 72 { 73 x=p[x][j]; 74 y=p[y][j]; 75 } 76 } 77 printf("%d\n",p[x][0]);} 78 } 79 /* 80 for(i=1;i<=n;i++) 81 { 82 cout<<i<<" "; 83 for(j=0;j<=19;j++) 84 cout<<p[i][j]<<" "; 85 cout<<endl; 86 }*/ 87 return 0; 88 }
- ST表
- 原理:dfs序在这两点之间的点中,深度最小的点为lca
- 所以记录dfs序