题目描述
已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。
输入
输入第一行包括一个整数n表示节点个数。
接下来n行每行一对整数对a和b表示a和b之间有连边。如果b是-1,那么a就是树的根。
第n+2行是一个整数m表示询问个数。
接下来m行,每行两个正整数x和y。
输出
对于每一个询问,输出1:如果x是y的祖先,输出2:如果y是x的祖先,否则输出0。
样例输入
10 234 -1 12 234 13 234 14 234 15 234 16 234 17 234 18 234 19 234 233 19 5 234 233 233 12 233 13 233 15 233 19
样例输出
1 0 0 0 2
题解
这题需要对模板做几个变化,首先要记录根节点,从根节点开始搜索,然后就是由于结点不是从1 2 3 4…n的形式,所以st数组的更新写进dfs中会更加方便,st[i][j]中的i不是从1开始线性递增的。最后就是如何判断祖孙关系,如果两个数的lca是其中一个数,就说明两者有祖孙关系,否则就没有祖孙关系。
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=4e5+10;
int num_edge=0,father[maxn],dep[maxn],st[maxn][25];
int first[maxn];//x点最后一条边的编号
struct Edge
{
int next;//下一条边的编号
int to;//这条边到达的点
//int dist;
}edge[maxn*2];
void add_edge(int from,int to)
{
edge[++num_edge].next=first[from];
edge[num_edge].to=to;
first[from]=num_edge;//将这一条边作为最后一条边,下一条边其实是上一次存储的边
}
void dfs(int x,int fa)
{
st[x][0]=fa;
for(int i=1;i<=20;i++)
st[x][i]=st[ st[x][i-1] ][i-1];
for(int i=first[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to==fa)
continue;
dep[to]=dep[x]+1;
dfs(to,x);
}
}
int get_lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=20;i>=0;i--)
if(dep[ st[x][i] ]>=dep[y])
x=st[x][i];
if(x==y)
return x;
for(int i=20;i>=0;i--)
{
if(st[x][i]!=st[y][i])
{
x=st[x][i];
y=st[y][i];
}
}
return st[x][0];
}
int main()
{
int n,q,root;
scanf("%d",&n);
memset(first,-1,sizeof(first));
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
if(y==-1)
root=x;
else
{
add_edge(x,y);//链式前向星
add_edge(y,x);
}
}
dep[root]=1;
dfs(root,0);
scanf("%d",&q);
while(q--)
{
int x,y;
scanf("%d %d",&x,&y);
int temp=get_lca(x,y);
if(temp==x)
printf("1\n");
else if(temp==y)
printf("2\n");
else
printf("0\n");
}
return 0;
}