CF GYM 2018 USP-ICMC I题
题目链接:
http://codeforces.com/gym/101875/problem/I
题目描述:
输入输出:
样例:
题目大意:
有n个人,m个询问,这n个人的编号为0~n-1,他们要去一个聚会,已知这n个人的每个人的唯一朋友是谁(若无朋友表示为-1),只有当这个唯一朋友去聚会时这个人才会去聚会。询问两个数x和y,问若x去了聚会,y是否一定回去这个聚会?
数据范围:
2 ≤ N ≤ 1 × 105
1 ≤ Q ≤ 2 × 105
ai = - 1 or 0 ≤ ai ≤ N - 1
0 ≤ xj, yj ≤ N - 1
解题思路:
因一个人若有朋友就只有一个唯一朋友,所以可能会有多个人把一个人当朋友,那我可以选择反向建图,将没有朋友(即ai=-1)的这个人当做根节点建一个一对多的树,可以用vector存。这道题询问x去聚会y是否一定去聚会,就是问x是不是y的孩子节点。从根节点跑DFS序,即跑一遍DFS,用l数组记录每个数最先出现的位置,r数组记录每个数最后出现的位置,对于每一个询问只需O(1)的复杂度,即判断一下x是不是y的孩子,若l[y]<=l[x] && r[x]<=r[y],即若x最先出现的位置与最后出现的位置,正好夹在y最先出现的位置与最后出现的位置的中间,则x是y的孩子。所以这个题主要要会DFS序。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
vector<int> G[100005];
int ind[100005], l[100005], r[100005];
int cnt;
void DFS(int x){
l[x] = ++cnt;
int len = G[x].size();
for(int i = 0;i < len;i++) DFS(G[x][i]);
r[x] = cnt;
}
int main(){
int n, m;
scanf("%d %d", &n, &m);
//memset(G, 0, sizeof(G));
for(int i = 0;i < n;i++){
int v;
scanf("%d", &v);
if(v != -1){
G[v].push_back(i);
ind[i]++;
}
}
for(int i = 0;i < n;i++){
if(ind[i] == 0) DFS(i);
}
while(m--){
int x, y;
scanf("%d %d", &x, &y);
if(l[y] <= l[x] && r[x] <= r[y]) printf("Yes\n");
else printf("No\n");
}
return 0;
}