首页 > 代码库 > 9018:1892:最近公共祖先
9018:1892:最近公共祖先
题目描述
编写一个程序,查找一棵含有n个节点的树中m组两个不同节点的最近公共祖先。
输入
第一行两个整数n和q,为树上节点的数目和查询数。节点标号为整数 1,2,...,n。之后n-1行每一行包含一对整数,代表边——第一个整数是第二个整数的父节点。请注意,一个具有n个节点的树有n-1条边。之后q行查询,每行两个整数,表示要查询的两个节点的标号。(输入保证有且只有一棵树)
输出
q行,每行一个整数,为此次查询的最近公共祖先的标号
样例输入
5 6
2 5
2 3
5 1
1 4
1 3
2 3
5 4
1 5
4 5
5 4
样例输出
2
2
5
5
5
5
提示
2<=n,m<=5*10^5
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
const int INF=500010;
vector <int> g[INF];
int fa[40][INF]={0};
queue <int>q;
int dep[INF];
bool use[INF]={false};
int root;
int n,m;
void bfs(int u){//使用的数
use[u]=true;q.push(u);//将u入队
while(!q.empty()){//当队伍非空时,深搜
u=q.front();//取出队首
for(int i=0;i<g[u].size();i++){//找这根链表的长度,从0到结束
int v=g[u][i];//v现在是这条链表的第i个
if(!use[v]){//如果没有使用过
dep[v]=dep[u]+1;//深度加一
q.push(v);//将其入队
}
}
q.pop();//将队尾入队
}
}
void bz(){//倍增
for(int j=1;j<=30;j++)//不清楚哦。。。。
for(int i=1;i<=n;i++)
fa[j][i]=fa[j-1][fa[j-1][i]];
}
int LCA(int u,int v){
if(dep[u]<dep[v])swap(u,v);//保证u为深度较深的
int dc=dep[u]-dep[v];//求出其差距,u要跳多少
for(int i=0;i<30;i++)//不超过2的30次方
if((1<<i)&dc)//如果2的i次方和dc的差二进制第一位为1
u=fa[i][u];//则u往上移2^i位。
if(v==u)return u;//如果深度相同,返回
for(int i=29;i>=0;i--){//
if(fa[i][u]!=fa[i][v]){//如果跳2的i次方步深度不同,就跳,否则就不跳
u=fa[i][u];//
v=fa[i][v];//
}
}
u=fa[0][u];//
return u;//
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)g[i].clear();//清空
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);//输入每次的子节点和父节点
g[a].push_back(b);//建树,将b放到a内;
fa[0][b]=a;//父节点
if(fa[0][a]==0)root=a;//他没有父节点,他为根
}
dep[root]=1;//根的深度为1,之后深度搜索
bfs(root);//深搜;
bz();//树上倍增,处理fa数组
int x,y;
for(int k=1;k<=m;k++){
scanf("%d%d",&x,&y);//不能用cin
printf("%d\n",LCA(x,y));//不能用cout
}
}
总结
千万不能用cin和cout,简直是可怕到了极点,还是用scanf和printf比较好
9018:1892:最近公共祖先