题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式:
第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:
输出包含M行,每行包含一个正整数,依次为每一个询问的结果。
输入输出样例
输入样例#1:
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
输出样例#1:
4
4
1
4
4
说明时空限制:1000ms,128M
数据规模:对于30%的数据:N<=10,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000
样例说明:
该树结构如下:
第一次询问:2、4的最近公共祖先,故为4。
第二次询问:3、2的最近公共祖先,故为4。
第三次询问:3、5的最近公共祖先,故为1。
第四次询问:1、2的最近公共祖先,故为4。
第五次询问:4、5的最近公共祖先,故为4。
故输出依次为4、4、1、4、4。
题解:标准LCA的倍增算法或者使用Tarjan算法。
//RMQ倍增模版 #include <cstdio> #include <cmath> #include <algorithm> #define N 1000005 int n,m,s,x,y,cnt; int first[N],next[N],v[N],dep[N/2],f[N][25]; bool vis[N/2]; using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); if (ch=='-') { f=-1; ch=getchar(); } while ((ch<'0')||(ch>'9')) ch=getchar(); while ((ch>='0')&&(ch<='9')) { x=x*10+ch-'0'; ch=getchar(); } return f*x; } inline void dfs(int root,int k) { dep[root]=k; vis[root]=1; for (int i=first[root];i;i=next[i]) if (!vis[v[i]]) { f[v[i]][0]=root; dfs(v[i],k+1); } } inline int find(int x,int y) { if (dep[x]<dep[y]) swap(x,y); for (int i=log2(n);i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; if (x==y) return x; for (int i=log2(n);i>=0;i--) if (f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } return f[x][0]; } int main() { freopen("1.in","r",stdin); n=read(),m=read(),s=read(); for (int i=1;i<n;i++) { x=read(),y=read(); next[++cnt]=first[x]; first[x]=cnt; v[cnt]=y; next[++cnt]=first[y]; first[y]=cnt; v[cnt]=x; } dfs(s,1); for (int j=1;j<=log2(n);j++) for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; for (int i=1;i<=m;i++) { x=read(),y=read(); if ((x==s)||(y==s)) printf("%d\n",s); else printf("%d\n",find(x,y)); } return 0; }
//Tarjan #include <cstdio> #define N 1000005 int n,m,s,x,y,cnt; int first[N],next[N],v[N],f[N/2],answer[N/2]; int first1[N],next1[N],v1[N],id[N]; bool vis[N/2]; using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); if (ch=='-') { f=-1; ch=getchar(); } while ((ch<'0')||(ch>'9')) ch=getchar(); while ((ch>='0')&&(ch<='9')) { x=x*10+ch-'0'; ch=getchar(); } return f*x; } inline int find(int x) { if (f[x]==x) return x; int t=find(f[x]); f[x]=t; return t; } inline void hb(int x,int y) { int xx=find(x),yy=find(y); if (xx!=yy) f[xx]=yy; } inline void tarjan(int root) { vis[root]=1; for (int i=first1[root];i;i=next1[i]) if (vis[v1[i]]) answer[id[i]]=find(v1[i]); for (int i=first[root];i;i=next[i]) if (!vis[v[i]]) { tarjan(v[i]); hb(v[i],root); } } int main() { n=read(),m=read(),s=read(); for (int i=1;i<n;i++) { x=read(),y=read(); next[++cnt]=first[x]; first[x]=cnt; v[cnt]=y; next[++cnt]=first[y]; first[y]=cnt; v[cnt]=x; } cnt=0; for (int i=1;i<=m;i++) { x=read(),y=read(); next1[++cnt]=first1[x]; first1[x]=cnt; v1[cnt]=y; id[cnt]=i; next1[++cnt]=first1[y]; first1[y]=cnt; v1[cnt]=x; id[cnt]=i; } for (int i=1;i<=n;i++) f[i]=i; tarjan(s); for (int i=1;i<=m;i++) printf("%d\n",answer[i]); return 0; }