本人最近刚接触到LCA,先是在vjudge的团队里做到了一到倍增LCA找父亲(codeforces 519E),然后求出有多少个点到访问的x,y这两个节点的距离相同。
洛谷题号:CF519E
链接:https://www.luogu.org/problemnew/show/CF519E
我有一个程序疯狂无限制第7个点爆MLE到现在都不知道为什么。
代码请见:https://www.luogu.org/discuss/show?postid=48843
希望有大佬能够指点迷津。 可以在洛谷评论也可以在这里评论,或者私信我的qq也行
QQ:2320993191
但是虽然说是一直MLE过不去这道题目,但是对LCA我初步有了一定的理解,然后我就按耐不住激动地心情,来到洛谷做了一下模板LCA。
这里我要讲两个注意点:
1.在我们在做dfs预处理f数组的时候,i不能倒着扫,因为每一个f[x][i]都来源于比自己要小的f[f[x][i-1]][i-1],所以如果倒着扫就会出现问题。我刚开始也就是因为这个错了。
2.当我们在把x节点和y节点跳到同一个深度以及之后达到同一深度再往上跳的时候,i都需要倒着扫,这里我们需要想一下,我举一个例子,如果我现在需要向上跳5个深度,那么如果我i=1的时候跳(1<<1)也就是2个深度的时候,我还需要跳3个深度,但是当我下一次要跳的时候 i已经等于2了,又因为(1<<2)等于4了,已经超过了3个深度所以已经不能跳了。所以必须是要倒着扫的,其次我们在想一下,即使我们倒着扫,我们也无法判断,这次往上跳完了之后到底是已经在我们所需要求的LCA只上,还是恰好是我们需要求的LCA,所以当f[x][i]==f[y][i]的时候我们干脆不跳,这样等我们操作完了之后,只需要在往上跳一个深度,那么那个节点就一定是我们所想要的LCA. 下面是我的AC代码,大家可以参考一下。如果有什么地方有问题欢迎大家提出,也随时欢迎大家来找我一起讨论OI问题,其他问题也可以啦哈哈哈哈哈哈哈,比如说抑郁不得志(这么大佬的你们肯定不会抑郁不得志的),我可以帮你解脱烦恼O(∩_∩)O哈哈~
(邻接表记录边的数组一定要开两倍!!!!因为是无向图,每条边会进两次)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000010;
int n,m,s,x,y,ans,top;
int first[N],next[N],get[N],deep[N];
int f[N][32];
void add(int x,int y){
get[++top]=y;
next[top]=first[x];
first[x]=top;
}
void dfs(int x,int fa,int deep_){
deep[x]=deep_;
f[x][0]=fa;
for (int i=1;(1<<i)<=deep_;++i)
f[x][i]=f[f[x][i-1]][i-1];
for (int i=first[x];i;i=next[i]){
int v=get[i];
if (v==fa) continue;
dfs(v,x,deep_+1);
}
}
void LCA(int a,int b){
if (deep[a]>deep[b]) swap(a,b); //a浅 b深
for (int i=31;i>=0;--i){
if (deep[b]-(1<<i)<deep[a]) continue;
b=f[b][i];
}
if (a==b) {
ans=a;
return;
}
for (int i=31;i>=0;--i){
if(f[a][i]==f[b][i]) continue;
a=f[a][i];
b=f[b][i];
}
ans=f[a][0];
}
int main(){
//freopen(".in","r",stdin);
scanf("%d%d%d",&n,&m,&s);
for (int i=1;i<=n-1;++i){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(s,0,1);
for (int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
LCA(x,y);
printf("%d\n",ans);
}
}