祖孙询问

祖孙询问


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;
}
posted @ 2019-08-14 09:50  to_the_end  阅读(420)  评论(0编辑  收藏  举报