一、啥?
LCA是啥?
简单来说,就是求两个节点的最近公共祖先。
二、咋求?
为方便,两个节点命名为x,y。fa为x父节点。令x的深度大于等于y的深度。
0、定义
dep[x]:x的深度
lg[x]:log2(x)向下取整
fa[x][i]:x的第2i代祖先
1、暴力
简单粗暴,直接将x的与y深度相同的祖先拉出,再将x,y同时往上一层提。
走到一起,就求出来了。
2、倍增
一层一层跳太慢了,考虑倍增。
首先dfs初始化。方程:
dep[x]=dep[fa]+1;
fa[x][i]=fa[fa[x][i-1]][i-1](2i=2i-1+2i-1)
lg[x]=lg[x-1]+(2lg[x-1]==x)
咋跳呢?
既然说了是倍增,那么每次必然跳的是$2^i$层。
可是不能这样子跳:$1,2,4,8,16 \cdot$
为什么呢?
例如你要跳$5$层,先跳$1$层,没问题;跳两层,也可以;到跳四层的时候,你发现数字大了,跳不到第$5$层了。
如果反过来:$\cdot 16,8,4,2,1$
先跳$4$层,可以;跳$2$层,大了;跳$1$层,刚好。
然后同上,两个节点跳到相同高度,再一起跳即可。
注意,不能直接跳到他们的公共祖先,因为有可能步子迈太大,跳到了LCA的祖先。所以要先跳到此时的祖先的下一层。
结束了。具体看代码:
#include#include #include #include using namespace std; struct zzz { int t, nex; }e[500010 << 1]; int head[500010], tot; void add(int x, int y) { e[++tot].t = y; e[tot].nex = head[x]; head[x] = tot; } int depth[500001], fa[500001][22], lg[500001]; void dfs(int now, int fath) { fa[now][0] = fath; depth[now] = depth[fath] + 1; for(int i = 1; i <= lg[depth[now]]; ++i) fa[now][i] = fa[fa[now][i-1]][i-1]; for(int i = head[now]; i; i = e[i].nex) if(e[i].t != fath) dfs(e[i].t, now); } int LCA(int x, int y) { if(depth[x] < depth[y]) swap(x, y); while(depth[x] > depth[y]) x = fa[x][lg[depth[x]-depth[y]] - 1]; if(x == y) return x; for(int k = lg[depth[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() { int n, m, s; scanf("%d%d%d", &n, &m, &s); for(int i = 1; i <= n-1; ++i) { int x, y; scanf("%d%d", &x, &y); add(x, y); add(y, x); } for(int i = 1; i <= n; ++i) lg[i] = lg[i-1] + (1 << lg[i-1] == i); dfs(s, 0); for(int i = 1; i <= m; ++i) { int x, y; scanf("%d%d",&x, &y); printf("%d\n", LCA(x, y)); } return 0; }