如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先.
输入格式:
第一行包含三个正整数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思路的话可以见我的转载博文:传送门这是我的代码实现~
%:pragma GCC optmize(3)//o3优化,尽量不要开 #include<bits/stdc++.h> #define maxn 1100010 #define maxm 1100130 using namespace std; int n,m,ss; struct aaa{int to,next;}edge[maxn]; struct bbb{int same,to,next,num;bool flag;bbb(){flag=0;}}s[maxm]; int head[maxn],shead[maxm],pre[maxn],book[maxn],ans[maxm]; int cnt=0; inline int read()//注意快读 { char c=getchar(); int num=0; while(!isdigit(c)) c=getchar(); while(isdigit(c)) { num=num*10+c-‘0‘; c=getchar(); } return num; } void add_edge(int u,int v) { edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; edge[++cnt].to=u; edge[cnt].next=head[v]; head[v]=cnt; } int cntt=0; void add_search(int u,int v,int w) { s[++cntt].to=v; s[cntt].next=shead[u]; s[cntt].num=w; shead[u]=cntt; s[++cntt].to=u; s[cntt].num=w; s[cntt].next=shead[v]; shead[v]=cntt; } int find(int x) { if(pre[x]==x) return x; else return pre[x]=find(pre[x]); } int merge(int x,int y)//注意谁和谁合并 { int fx=find(x),fy=find(y); if(pre[fy]!=fx) { pre[fy]=fx; return 1; } return 0; } void tarjan(int point,int f) { for(int i=head[point];i;i=edge[i].next) if(edge[i].to!=f&&!book[edge[i].to])//不能返回之前走过的点——父节点 { tarjan(edge[i].to,point); merge(point,edge[i].to); book[edge[i].to]=1; } for(int i=shead[point];i;i=s[i].next) if(book[s[i].to]&&!s[i].flag) { ans[s[i].num]=find(s[i].to); s[i].flag=1;//走过的点标记 int tt=i;if(tt%2)tt++;else tt--; s[tt].flag=1;//因为是双向储存所以两边都要去掉 } } int main() { scanf("%d%d%d",&n,&m,&ss); for(int i=1;i<n;i++) { int t1,t2; t1=read();t2=read(); add_edge(t1,t2); } for(int i=1;i<=n;i++) pre[i]=i; for(int i=1;i<=m;i++) { int t1,t2; t1=read();t2=read(); add_search(t1,t2,i); } tarjan(ss,0);//虚构一个父节点 for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
时间: 2024-10-01 03:20:24