I d i o t 的 间 谍 网 络 Idiot\ 的间谍网络 Idiot 的间谍网络
题目链接:jzoj 3910
题目大意
有三种操作:
- 读入: 1 x y 1\ x\ y 1 x y:特工 y y y成为特工 x x x的直接领导。数据保证在此之前特工 x x x没有直接领导;
- 读入: 2 x 2\ x 2 x:特工 x x x策划了一起特别行动,然后上报其直接领导审批,之后其直接领导再上报其直接领导的直接领导审批,以此类推,直到某个特工审批后不再有直接领导;
- 读入: 3 x y 3\ x\ y 3 x y:询问特工 x x x 是否直接策划或审批过第 y y y次特别行动。所有特别行动按发生时间的顺序从 1 1 1开始依次编号。数据保证在询问之前,第 y y y次特别行动已经发生过。(输出 Y E S YES YES或者 N O NO NO)
样例输入
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
数据范围
对于
30
%
30\%
30% 的数据,
n
<
=
3
∗
1
0
3
n <= 3 *10^3
n<=3∗103,
m
<
=
5
∗
1
0
3
m <= 5* 10^3
m<=5∗103;
对于
60
%
60\%
60% 的数据,
n
<
=
2
∗
1
0
5
n <=2 * 10^5
n<=2∗105,
m
<
=
2
∗
1
0
5
m <= 2 * 10^5
m<=2∗105;
额外
20
%
20\%
20% 的数据,保证在任意时刻,整张间谍网络由若干条互不相交的链构成;
对于
100
%
100\%
100% 的数据,
n
<
=
5
∗
1
0
5
n <= 5 * 10^5
n<=5∗105,
m
<
=
5
∗
1
0
5
m <= 5 * 10^5
m<=5∗105;
C
+
+
C++
C++选手的程序在评测时使用编译选项
−
W
l
;
−
−
s
t
a
c
k
=
104857600
-Wl;--stack\ =\ 104857600
−Wl;−−stack = 104857600。
思路
这是一道用到并查集和
d
f
s
dfs
dfs的问题。
看
A
A
A知道
B
B
B干的第
x
x
x件事,要满足两个点:
- A A A是 B B B的祖先(或它自己)
- B B B干第 x x x件事时, A A A和 B B B已经连通。(或者说 A A A已经是 B B B的祖先(或它本身))
首先,我们来康康第
1
1
1个要求,这个要求我们可以用
d
f
s
dfs
dfs序来实现。
接着,第
2
2
2个要求,可以读入完之后,重新再“读入一遍”。操作
1
1
1就并查集确立祖先,这样子操作
2
2
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++) {//求出的dfs序,以便确定某个点是否是某一个点的祖先(或它本身)
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] = 1;//标记
what++;//到下一个询问
}
}
}
for (int i = 1; i <= kk; i++)
if (ans[i]) printf("YES\n");//知道
else printf("NO\n"); //不知道
return 0;
}