可能是人生的第一篇博客?竟然被我拿来记代码了。。
题源:https://www.luogu.com.cn/problem/P3379
没开2倍跳过非法范围WA了三次,希望以后回来看还能看懂
#include <iostream> #include <stdio.h> #include <cstring> #define maxn 500005 using namespace std; struct Edge { int next, to; } edge[maxn * 2]; //开双倍前向星,存储两个相反有向边(无法确定父子关系) int head[maxn], dep[maxn], f[maxn][31], num = 0; //head意为以该下标为起点的边集中最后加入的在edge中对应的下标,f[n][i]意为n最近的第2^i个祖先编号 //num初始化为0,将0预留为dfs的假想起点 void add_edge(int u, int v) { // 双向存储 edge[++num].next = head[u]; edge[num].to = v; head[u] = num; //注意前一个head[u]和后一个head[u]的区别,前一个意为上一条边的编号,后一个更新为新加入的编号,注意顺序 edge[++num].next = head[v]; edge[num].to = u; head[v] = num; } void dfs(int u, int fa) { dep[u] = dep[fa] + 1; for (int i = 1; (1 << i) <= dep[u]; ++i) { f[u][i] = f[f[u][i - 1]][i - 1]; //2^i-1 + 2^i-1 = 2^i,递推预处理出树 } for (int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; if (v == fa) continue; //根据父子关系,排除另一条有向边 f[v][0] = u; dfs(v, u); } } int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); //使x,y位于同一深度 for (int i = 30; i >= 0; --i) { if (f[x][i] == 0) continue; //超出范围,即等于0要跳过 if (dep[f[x][i]] >= dep[y]) x = f[x][i]; if (x == y) return x; } for (int i = 30; i >= 0; --i) { if (f[x][i] == 0 || f[y][i] == 0) continue; //同上 if (f[x][i] != f[y][i]) //在lca以上的点都满足相等条件,因为lca的祖先也是x,y的祖先 { x = f[x][i]; y = f[y][i]; } } return f[x][0]; //由于二进制数的特性,每一位数只有01两种可能,从首位不断逼近下一位(中间过程是假使后尾全是0)直至个位可得到原数 } int main() { int n, m, s, x, y; memset(head, 0, sizeof(head)); memset(dep, 0, sizeof(dep)); memset(f, 0, sizeof(f)); //注意初始化为0 scanf("%d%d%d", &n, &m, &s); for (int i = 1; i < n; ++i) { scanf("%d%d", &x, &y); add_edge(x, y); } dfs(s, 0); //辅助0 for (int i = 0; i < m; ++i) { scanf("%d%d", &x, &y); printf("%d\n", lca(x, y)); } }