还是济南集训的内容,让人头秃(不得不说两个老师讲了两遍我勉勉强强才搞懂一点点)
首先来看:
LCA的含义
Least Common Ancestors
LCA就是最近公共祖先,至于它的含义,我觉得例题写的看起来会更清楚,请看:
好的,明白了它的含义后,我们很容易想到朴素算法:
询问(x,y)的最近公共祖先,可以先向上枚举x的所有祖先,
保存在数组Anc[]中。然后以相同的方法向上枚举y的所有祖
先,当第一次发现有y的某个祖先k出现在Anc[]中,则输出
k,算法结束。
此时,每次查询复杂度为
O( N )
(TLE警告哦)
那么其他方法呢,请看,这里介绍两种,
树上倍增,与
树链剖分(会在下一篇博客里写到)
树上倍增算法
核心思想:
- 令F[x][n]表示x的2^n级祖先是谁.
- 所以:F[x][n] = F[F[x][n – 1]][n – 1].
- 对于两个点x, y.,求他们的LCA
- 先把x, y提到同一高度.(方便向上进行倍增)
- N从大到小枚举.(从高往低跳)
- 查询F[x][n], F[y][n]是不是相等(比较倍增后的祖先,防止误判)
- 如果是的话说明n太大了,把n改小点.(最近公共祖先的祖先一定是他们的共同祖先)
- 不是的话就说明n不大,可以把x, y上移.(这个很容易理解吧)
原理,如下图(图片来自
老师的ppt)
(它其实应该有个动图然而我不知道动图怎么传):
看明白思路了吗,思路还是可以懂得吧,那我们来看代码,理解代码基本就能写了!
(不过我之前也始终看不明白代码就是了,思路都懂代码不会打,我可真是个小垃圾哦)
#include<cstdio> #include<iostream> #include<cmath> using namespace std; const int maxn = 500005; const int maxe = 1000005; int n,m,root; struct line { int from,to; line(){}//空构造函数 line p; line(int A,int B){ //构造函数 line L=line(1,2); from=A;to=B; } }edge[maxe]; //上面是新建一个树 int last[maxn],_next[maxe],e; //last[x]表示以x为起点的最后一条边(的编号) //_next[i]表示与第i条边起点相同的上一条边(的编号) void add_edge(int x,int y) { edge[++e]=line(x,y); _next[e]=last[x]; last[x]=e; } //存边 int Fa[maxn][35],Dep[maxn]; void dfs(int x,int fa) { int i,k,y; Fa[x][0]=fa;//当前节点x的父亲节点fa Dep[x]=Dep[Fa[x][0]]+1; //x的深度是它父亲节点的深度+1 //记录当前节点的深度 k=ceil(log(Dep[x])/log(2)); //ceil函数是向上取整 //x往上倍增的上限 for(i=1;i<=k;i++)Fa[x][i]=Fa[Fa[x][i-1]][i-1]; //倍增计算祖先 ,记录 for(int i=last[x];i;i=_next[i])//枚举与x相邻的边 { int v=edge[i].to; if(v!=fa)dfs(v,x); } } int LCA(int x,int y) { int i,k,s; s=ceil(log(n)/log(2)); //该树倍增最大可能的上限 if(Dep[x]<Dep[y])swap(x,y); //交换x和y的值 /x往上走k层,让x与y处于同一层 // k=Dep[x]-Dep[y]; for(i=0;i<=s;i++) if(k&(1<<i))x=Fa[x][i]; if(x==y)return x; //x==y时,x就是最近公共祖先 /// s=ceil(log(Dep[x])/log(2)); //计算向上倍增的上限 for(i=s;i>=0;i--) if(Fa[x][i]!=Fa[y][i]){ x=Fa[x][i]; y=Fa[y][i]; } return Fa[x][0]; } int main() { int i,j,k; cin>>n>>m>>root; for(i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x);//它是树,也就是无向图,所以存两次边 } dfs(root,0);//预处理 for(i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",LCA(x,y)); } }
OK吗?
这里建议去练练板子,指路-> https://www.luogu.org/problem/P3379
(哇我居然可以写蓝题了哎!!可喜可贺)
先到这里,
如有问题欢迎指正
感谢观看 ありがとうございます