题目链接
https://www.luogu.com.cn/problem/P3379
解题思路
ST表 + 欧拉序 。
通过欧拉序会发现根结点总在中间,而根结点是该段序列深度最小的点。
因此两个点的LCA,就是在该序列上两个点第一次出现的区间内深度最小的那个点。
细节见代码,主要是注意下标含义。
AC代码
#include<bits/stdc++.h> using namespace std; const int N=5e5+100; int n,m,rt,Time,a,b; int path[N<<1],pos[N<<1][20],dp[N],in[N],st[N<<1][20]; vector<int> e[N]; inline int read() { int x; char ch='*'; while(!isdigit(ch=getchar())); x=ch-'0'; while(isdigit(ch=getchar())) x=(x<<3)+(x<<1)+ch-'0'; return x; } void dfs(int x,int fa,int deep) { path[++Time]=x;//入,记录欧拉序路径,路径的第Time个是第i个节点 if(dp[x]==0) dp[x]=deep;//dp[i]表示i节点在树中的深度 if(in[x]==0) in[x]=Time;//in[i]表示i节点第一次出现在欧拉序中的位置 for(int i=0;i<e[x].size();i++) { if(fa==e[x][i]) continue; dfs(e[x][i],x,deep+1); path[++Time]=x;//出,记录欧拉序路径 } } void CreateST()//创建st表 { for(int i=1;i<=Time;i++) st[i][0]=dp[path[i]],pos[i][0]=i;//pos两维的含义与st相同,记录的是区间深度最小的节点在欧拉序路径中的编号//初始化st表 for(int j=1;(1<<j)<=Time;j++) for(int i=1;i+(1<<j-1)-1<=Time;i++) if(st[i][j-1]>st[i+(1<<j-1)][j-1]) st[i][j]=st[i+(1<<j-1)][j-1],pos[i][j]=pos[i+(1<<j-1)][j-1]; else st[i][j]=st[i][j-1],pos[i][j]=pos[i][j-1]; } int Query(int l,int r) { if(l>r) swap(l,r); int k=0;while(l+(1<<k+1)-1<=r)k++; return st[l][k]<st[r-(1<<k)+1][k]?path[pos[l][k]]:path[pos[r-(1<<k)+1][k]];//path下标的含义为欧拉序,path[i]的含义为欧拉序为i的节点的是哪个,所以pos要作为path的下标访问。 } int main() { n=read(),m=read(),rt=read(); for(int i=1;i<n;i++) { a=read(),b=read(); e[a].push_back(b); e[b].push_back(a); } dfs(rt,-1,1); CreateST(); while(m--) { a=read(),b=read(); printf("%d\n",Query(in[a],in[b]));//Query传入的参数为在第一次在欧拉路径中出现的位置 } return 0; }