CF 466E Information Graph

题目描述

http://codeforces.com/problemset/problem/466/E

简要题意:在某公司有 $n$ 个员工,第 $0$ 天时员工之间没有任何关系,接下来 $m$ 天,每天可能会发生三种事情,第一种事情是 $y$ 成了 $x$ 的上司,保证 $x$ 在这之前没有上司;第二种事情是员工 $x$ 得到了一份文件,$x$ 会将其传递给他的上司,他的上司会将文件继续传递给他的上司,直到没有上司;第三种事情是询问员工 $x$ 在第 $y$ 天有没有看过文件

$n,m\le 10^5$

Solution

注意到操作只有连边且保证是一棵树,我们可以考虑逆向进行并查集的撤销

注意到在逆序的时候一次询问相当于问在之前的某个时间,$x$ 是否是 $y$ 的父亲

然后问题就解决了,时间复杂度 $O(n\alpha(n))$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>
#include <vector>
#include <tuple>
#define maxn 100010
using namespace std;

int n, m, k, bl[maxn], t[maxn];

struct Edge {
int to, next;
} e[maxn]; int c1, head[maxn], du[maxn];
inline void add_edge(int u, int v) {
e[c1].to = v; e[c1].next = head[u]; head[u] = c1++;
}

int in[maxn], out[maxn];
void dfs(int u) {
static int cnt = 0;
in[u] = ++cnt;
for (int i = head[u]; ~i; i = e[i].next) dfs(e[i].to);
out[u] = cnt;
}

int sz[maxn], fa[maxn], st[maxn], top;
void init_fa(int n) { for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1; }

int find(int x) { return fa[x] == x ? x : find(fa[x]); }

inline void merge(int x, int y) {
int fx = find(x), fy = find(y);
if (sz[fx] < sz[fy]) swap(fx, fy);
fa[fy] = fx; sz[fx] += sz[fy]; st[++top] = fy;
}

inline void undo() {
int x = st[top--];
sz[fa[x]] -= sz[x];
fa[x] = x;
}

struct Query {
int opt, x, y;
} Q[maxn];

vector<tuple<int, int, int>> A[maxn];

int ans[maxn];
int main() { fill(head, head + maxn, -1);
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m; init_fa(n);
for (int i = 1; i <= m; ++i) {
int x, y; cin >> Q[i].opt >> x;
if (Q[i].opt == 1) {
cin >> y; ++du[x];
add_edge(y, x); merge(x, y);
}
else if (Q[i].opt == 2) ++k, bl[k] = x, t[k] = i;
else cin >> y;
Q[i].x = x; Q[i].y = y;
}
for (int i = 1; i <= n; ++i)
if (!du[i]) dfs(i);
for (int i = 1; i <= m; ++i)
if (Q[i].opt == 3) {
int x = Q[i].x, y = Q[i].y;
if (in[x] <= in[bl[y]] && in[bl[y]] <= out[x]) A[t[y]].emplace_back(x, bl[y], i);
else ans[i] = -1;
}
for (int i = m; i; --i) {
if (Q[i].opt == 1) undo();
for (auto t : A[i]) {
int x, y, k; tie(x, y, k) = t;
ans[k] = find(x) == find(y) ? 1 : -1;
}
}
for (int i = 1; i <= m; ++i)
if (ans[i] == 1) cout << "YES\n";
else if (ans[i] == -1) cout << "NO\n";
return 0;
}