Idiot 的间谍网络
题目大意
就是有三种类型
类型1 x y:y 成为x 的直接领导;
类型2 x:x 策划了一起行动,然后上报其直接领导审批,之后其直接领导再上报其直接领导的直接领导审批,以此类推,直到某个特工审批后不再有直接领导;
类型3 x y:询问x 是否直接策划或审批过第y 次特别行动。所有特别行动按发生时间的顺序从1 开始依次编号
(有就输出YES 否则输出NO)
输入
一行,n 和 m 表示人数和多少种事件
输出
每一次询问后的答案
输入样例
6 12
2 1
1 4 1
3 4 1
1 3 4
2 3
3 4 1
2 3
3 4 2
3 1 1
3 1 3
3 1 2
1 2 4
输出样例
NO
NO
YES
YES
YES
YES
解题思路
这道题用并查集和
D
F
S
DFS
DFS 序来做,对于一个点是否为另一个点祖先的询问,其实可以直接使用 dfs 序来解决
操作1就并查集确立祖先,这样子操作2的时候就可以看他们的祖先是否相同。
相同就是连通的,否则不连通
程序如下
#include<cstdio>
#include<algorithm>
using namespace std;
struct note {
int to, next;
}e[500001];
struct read {
int what, num, who;
}a[500001];
int n, m, le[500001], z[500001], x[500001], y[500001], k, kk, kkk, in[500001], out[500001], father[500001];
bool fa[500001], ans[500001];
bool cmp(read x, read y) {
return x.what < y.what;//比较大小
}
void dfs(int now) {//DFS序
in[now] = ++in[0];
for (int i = le[now]; i; i = e[i].next) {
dfs(e[i].to);
}
out[now] = ++out[0];
}
int find(int now) {//查找是否为父亲
if (father[now] == now) return now;
return father[now] = find(father[now]);
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d", &z[i]);
if (z[i] == 1) {
scanf("%d %d", &x[i], &y[i]);
e[++k] = (note){x[i], le[y[i]]}; le[y[i]] = k;//建邻接表
fa[x[i]] = 1;//找出根节点
}
else if (z[i] == 2) {
scanf("%d", &x[i]);
}
else if (z[i] == 3) {
scanf("%d %d", &x[i], &y[i]);
a[++kk] = (read){y[i], ++kkk, x[i]};//记录
}
}
sort(a + 1, a + kk + 1, cmp);//排序任务
for (int i = 1; i <= n; i++) {
father[i] = i;
if (!fa[i]) {
dfs(i);
}
}
int what_num = 0, what = 1;//初始化
for (int i = 1; i <= m; i++) {
if (z[i] == 1) father[find(x[i])] = find(y[i]);//连线
else if (z[i] == 2) {
what_num++;
while (what <= kk && a[what].what <= what_num) {
if (find(a[what].who) == find(x[i]) && in[a[what].who] <= in[x[i]] && out[a[what].who] >= out[x[i]])//判断是否连通
ans[a[what].num] = i;//标记
what++;
}
}
}
for (int i = 1; i <= kk; i++)
if (ans[i]) printf("YES\n");
else printf("NO\n");
return 0;
}