首页 > 代码库 > 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:最近公共祖先