一个月以前学的最近公共祖先。一直以为我理解的最够深刻了,直到遇见真的比较复杂的题之后,才发现自己的漏洞。
那么今天就借助一道模板题来总结一下吧。
下面是洛谷模板的题面。
下面是样例及解释。
Input
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
Output
4
4
1
4
4
那么接下来就详细说说LCA是怎么回事吧。
首先是一般的LCA。
那么我们就先举个栗子。
比如4和16的LCA就是3.而对于9和10的LCA则是7.对于17和18则又是3。
那么暴力做法就很显然了不是吗?
暴力做法
我们就以17和18为例。既然是要找LCA,那么我们就让他们往上去走。
17−>14−>10−>7−>3
18->16->12->8->5->318−>16−>12−>8−>5−>3
第一次遇到的地方就是LCA(17,18)的值了。DFS暴力实现啊?
如果你觉得会这些就足够了的话(那您可就是神了啊),TLE欢迎你。大多数题一般是不会很快(-->TLE)的。
不信我们就举个栗子吧(还是上图),比如4和18.显然这样暴力是不太好的。我们显然是希望4可以等18上去了之后再走。
那么就有一种很优秀(玄学)的LCA了。
倍增LCA
当你看到这里,这题才刚刚开始啊。
那么首先先说倍增吧(有专门讲倍增的文章这里就简单说一下啦)。
!!倍增
倍增就是按照2的n次幂来往上走。但是我们一般是从大往小跳,当用大的跳过了之后,就用小的再跳回来(好蠢的样子)。
还是举个栗子吧(还是17和18)
17−>3
18−>5−>3
这样明显就是快了不少啊。复杂度是O(nlogn);对于大多数题来说就足够了。
回到LCA
所以对于倍增LCA来说,我们就需要记录一下每一个节点的幂次方爸爸是谁了啊。
那么我们跑一遍dfs就解决了。(deep是节点的深度,fa是存某数的幂次方爸爸的)
void dfs(int f,int fath) {
deep[f]=deep[fath]+1;
fa[f][0]=fath;
for(int i=1;(1<
然后我们就可以上LCA了。
在此之前,我喜欢先加一个常数的优化。(当然你也可以不加,直接套用log2(x)->x是次方,就应该也可以啊)
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<
然后就是LCA啦,我们想把他们都调到一个高度再找,这样就可以实现了。
int LCA(int x,int y) {
if(deep[x]deep[y])
x=fa[x][lg[deep[x]-deep[y]]-1];
if(x==y)
return x;
for(int k=lg[deep[x]]-1;k>=0;k--)
if(fa[x][k]!=fa[y][k]) {
x=fa[x][k];
y=fa[y][k];
}
return fa[x][0];
}
很好,我自以为讲的还不错,勉强看吧(毕竟语文不好)。下面放完整版。
Code(代码风格2.1版)
#include
#include
#include
#include
#include
using namespace std;
const int MA=5e5+1;
struct ss{
int t,nex;
}edge[2*MA];
int n,m,s,tot;
int fa[MA][22];
int lg[MA],head[MA],deep[MA];
int read() {
int x=0;
bool flag=0;
char ch=getchar();
if(ch=='-')
flag=1;
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9') {
x*=10;
x+=ch-'0';
ch=getchar();
}
if(flag) return -x;
return x;
}//快读压行什么的,我才不要 傲娇】
void add(int x,int y) {
edge[++tot].t=y;
edge[tot].nex=head[x];
head[x]=tot;
}
void dfs(int f,int fath) {
deep[f]=deep[fath]+1;
fa[f][0]=fath;
for(int i=1;(1<deep[y])
x=fa[x][lg[deep[x]-deep[y]]-1];
if(x==y)
return x;
for(int k=lg[deep[x]]-1;k>=0;k--)
if(fa[x][k]!=fa[y][k]) {
x=fa[x][k];
y=fa[y][k];
}
return fa[x][0];
}
int main()
{
n=read();
m=read();
s=read();
for(int i=1;i
然后我这题还能用 树链剖分,还有约束RMQ求LCA,以及tarjan求LCA(这些我全都不会)。之后会了的话会回来不上的,有兴趣的可以之后在自学一下啦。
那么就这样啦。谢谢