洛谷p3379
题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式:
第一行包含三个正整数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
#include <cstdio>
#include <iostream>#include <algorithm>
#include <cstring>
#define M 500000*2+10
#define N 500000*2+10
using namespace std;
struct node
{
int to,next;//to 出边
//next 从这个节点出发的的上一条边的编号
}e[M];
int tot,st[M];//tot 边的编号
//st【i】表示从i出发的最后一条边的编号
int n,m,root,x,y;//n条边 m次询问 root为根节点
int f[N][20];
int dfs_list[N],depth[N],first[N];
//dfs_list【】欧拉迹
//depth【i】表示dfs_list[i]的深度
//first【now】表示now节点在欧拉迹中出现的第一个位置
void add(int x,int y)//邻接表
{
e[++tot].to=y;
e[tot].next=st[x];
st[x]=tot;
}
int pow_2(int j)
{
return (1<<j);//2^j
}
void rmq_init()
{
for (int i=1;i<=2*n-1;i++)
f[i][0]=i;
//从dfs_list【i】到i之后2^0步的最小值
int i=1,j=1;
for (int j=1;pow_2(j)<=2*n-1;j++)
for (int i=1;i+pow_2(j)<=2*n-1;i++)
{
int a=depth[f[i][j-1]];
int b=depth[f[i+pow_2((j-1))][j-1]];
if (a<b)
f[i][j]=f[i][j-1];
else
f[i][j]=f[i+pow_2((j-1))][j-1];
}
}
int rmq_query(int l,int r)
{
int pos;
for (pos=0;pow_2(pos)<(r-l+1);pos++)/*!*/;/*!*/
pos--;
int a=depth[f[l][pos]];
int b=depth[f[r-pow_2(pos)+1][pos]];
if (a<b)
return dfs_list[f[l][pos]];
else
return dfs_list[f[r-(1<<pos)+1][pos]];
}
int dfs_clock=0;//欧拉迹中点的个数
void dfs(int now,int pre,int deep)//搜索欧拉迹
{
dfs_list[++dfs_clock]=now;
depth[dfs_clock]=deep;
if (!first[now])
first[now]=dfs_clock;
for (int i=st[now];i;i=e[i].next)//遍历
if (e[i].to!=pre)
//对于根节点来说没有绝对的限制
{
dfs(e[i].to,now,deep+1);
dfs_list[++dfs_clock]=now;
depth[dfs_clock]=deep;
}
}
main()
{
scanf("%d%d%d",&n,&m,&root);
for (int i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(root,0,0);
rmq_init();
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
if (first[x]>first[y]) swap(x,y);
//需要从前往后搜
printf("%d\n",rmq_query(first[x],first[y]));
}
}