祖孙询问
祖孙询问
Description
已知一棵\(n\)个节点的有根树。有\(m\)个询问。每个询问给出了一对
节点的编号\(x\)和\(y\),询问\(x\)与\(y\)的祖孙关系。
Input Format
输入第一行包括一个整数n表示节点个数。
接下来n行每行一对整数对\(a\)和\(b\)表示\(a\)和\(b\)之间有连边。如果\(b\)是\(-1\),那么\(a\)就是树的根。
第\(n+2\)行是一个整数\(m\)表示询问个数。
接下来\(m\)行,每行两个正整数\(x\)和\(y\)。
Output Format
对于每一个询问,输出1:如果\(x\)是\(y\)的祖先,输出2:如果\(y\)是\(x\)的祖先,否则输出\(0\)。
Sample Input
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
Sample Output
1
0
0
0
2
Hint
对于\(30%\)的数据,\(n,m≤1000\)。
对于\(100%\)的.据,\(n,m≤40000\),每个节点的编号都不超过\(40000\)。
Solution
首先这题应该是一个\(LCA\)模板题,但是我们这里考虑用\(dfs\)来做。
其实这题的主要思想应该是一个时间戳的应用。
我们记录当前搜索到这个点的时间(变灰时间)和递归到这个点的时间(变黑时间)
然后我们将变灰时间和变黑时间进行比较就可以找出两个点的关系
#include<bits/stdc++.h>
#define Re register int
using namespace std;
int N,M,a,b,Num,head[80005],Start,Dep[40005],Ret[40005],Time;
bool Check[40005];
struct Edge{
int To,Next;
}edge[80005];//双向建边数组记得开两倍
inline void Add_Edge(int u,int v){
edge[++Num].Next=head[u];
edge[Num].To=v;
head[u]=Num;
}
inline void Dfs(int Now){
Dep[Now]=++Time; Check[Now]=true; //变灰的时间
for (Re i=head[Now];i;i=edge[i].Next) if (!Check[edge[i].To]) Dfs(edge[i].To);
Ret[Now]=++Time; //变黑的时间
}
int main(){
scanf("%d",&N);
for (Re i=1; i<=N; ++i){
scanf("%d%d",&a,&b);
if (b==-1) Start=a;
else {
Add_Edge(a,b);Add_Edge(b,a);
}
}
Dfs(Start);
scanf("%d",&M);
for (Re i=1; i<=M; ++i){
scanf("%d%d",&a,&b);
if (Dep[a]<Dep[b]&&Ret[a]>Ret[b]) printf("1\n");
else if (Dep[a]>Dep[b]&&Ret[a]<Ret[b]) printf("2\n");
else printf("0\n");
}
return 0;
}
第二种方法......
就是正版一点的\(LCA\)
\(LCA\)模板在上面随笔的转发
用\(LCA\)判断两个点的高低,然后让低的那个点往上跳,一直跳到两个点在同一层,记住这里指的是同一层;
然后在判断这两个点是否重合,如果重合就可以输出答案
答案是\(1\)还是\(2\)那么就是看两个点的高低......
#include<bits/stdc++.h>
#define Re register int
using namespace std;
int N,M,a,b,Num,head[80005],Root;
int f[40005][21],d[40005];
bool Check[3];
struct Edge{
int To,Next;
}edge[80005];
inline void Add_Edge(int u,int v){
edge[++Num].Next=head[u];
edge[Num].To=v;
head[u]=Num;
}
inline void dfs(int Now,int fa){
d[Now]=d[fa]+1; f[Now][0]=fa;
for (Re i=1; (1<<i)<=d[Now]; ++i) f[Now][i]=f[f[Now][i-1]][i-1];
for (Re i=head[Now];i;i=edge[i].Next) if (edge[i].To^fa) dfs(edge[i].To,Now);
}
inline void LCA(int a,int b){
if (d[a]>d[b]) swap(a,b),Check[0]=true;
for (Re i=20; i>=0; --i) if (d[a]<=d[f[b][i]]) b=f[b][i];
if (a==b) Check[1]=true;
}
int main(){
scanf("%d",&N);
for (Re i=1; i<=N; ++i){
scanf("%d%d",&a,&b);
if (b==-1) Root=a;
Add_Edge(a,b);
Add_Edge(b,a);
}
dfs(Root,0);
scanf("%d",&M);
for (Re i=1; i<=M; ++i){
memset(Check,false,sizeof(Check));
scanf("%d%d",&a,&b);
LCA(a,b);
if (Check[0]&&Check[1]) printf("2\n");
else if (!Check[0]&&Check[1]) printf("1\n");
else printf("0\n");
}
return 0;
}