P3379 【模板】最近公共祖先(LCA)

mac2024-06-06  40

Link

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

Input

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

Output

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。


RT。 原本,看到这种题,想到的应该是从树的下面逐个上跳去寻找。 LCA就是从下往上倍增跳跃。可以省掉许多空余的步骤。 直接跳(可以跳的最大k次方)会节省时间。比如找【5=(二的次方的和)?】,【1+2+?】倒回再【1+4】就比直接【4+1】慢了。(啊啊啊来自luogu某题解) 详见代码算了。

#include<cstdio> #include<algorithm> using namespace std; int n,m,s,t; int l[500001],jump[500001],Deep[500001],father[500001][101]; struct asdf{ int to,next; } A[1000001]; void read(){ //读入 int x,y; scanf("%d%d%d", &n, &m, &s); for(int i = 1; i < n; ++i){ scanf("%d%d", &x, &y); A[++t] = (asdf){y,l[x]}; l[x] = t; //邻接表存储 A[++t] = (asdf){x,l[y]}; l[y] = t; } int k = 1; jump[1] = 1; //预处理jump。jump[i]为在深度为i的时候可以跳2^(i-1)的步数 for(int i = 2; i <= n; ++i){ jump[i] = jump[i-1]; if(i / k == 2){ k*=2; ++jump[i]; } } } void dfs(int now,int fu){ //当前点和父亲//dfs求某节点跳2^k步是哪个节点 father[now][0] = fu; //跳2^0步 Deep[now] = Deep[fu] + 1; //深度统计 for(int i = 1; i <= jump[Deep[now]]; ++i) father[now][i] = father[father[now][i-1]][i-1]; //从当前点跳2^i步=跳了2^(i-1)步的点后再跳2^(i-1)步 for(int i = l[now]; i; i = A[i].next) if(Deep[A[i].to] == 0) dfs(A[i].to,now); } int kmp(int x,int y){ if(Deep[x]>Deep[y]){ //让y的深度大于x的深度 int l = x; x = y; y = l; } while(Deep[x] != Deep[y]) y = father[y][jump[Deep[y] - Deep[x]] - 1]; //让x和y跳到同一高度 //y跳的时候就有倍增跳跃 if(x == y) return x; for(int i = jump[Deep[x]]-1; i>=0; --i) //跳跃 if(father[x][i] != father[y][i]){ //如果父亲不一样就跳过去 //如果父亲一样可能会跳过头 //所以我们的目标是最近公共祖先的子节点 x = father[x][i]; y = father[y][i]; } return father[x][0]; //返回 } int main(){ read(); dfs(s,0); for(int i = 1; i <= m; ++i){ int x,y; scanf("%d%d",&x,&y); printf("%d\n",kmp(x,y)); //输出 } }
最新回复(0)