CSP-S 2022 星战
Hash,思维
反攻时刻可以抽象为:
- 每个点出发都能一直走下去,即从一个点出发能够到达一个环。
- 每个点的出度为 \(1\)。
如果所有点的出度都为 \(1\),那么无论从哪个点开始走都能一直走下去,也就是满足第一个条件。那么维护当前图上所有边的起点的可重集合,当这个集合为 \(\{ 1, 2, 3, \dots, n \}\) 时,说明当前是一个反攻时刻。
考虑 Hash,我们可以给每个点赋一个随机权值 \(w_i\),那么记当前集合的哈希值就是就是集合内所有点的权值和。设当前集合的哈希值为 \(S\),那么当 \(S = \sum\limits_{i = 1}^{n}{w_i}\) 时说明当前是一个反攻时刻。
对于四个操作,设 \(s_u\) 表示以 \(u\) 为终点所有边的起点的权值和,显然 \(s_u = \sum\limits_{(v \to u) \in G}{w_v}\);\(a_u\) 表示以 \(u\) 为终点的所有被摧毁的边的起点的权值和。则
- 对于 \(t = 1\),摧毁 \(u \to v\)。\(S\) 减少 \(w_u\),\(a_v\) 增加 \(w_u\)。
- 对于 \(t = 2\),摧毁所有以 \(u\) 为终点的边。\(S\) 减少 \(s_u - a_u\),\(a_u\) 变成 \(s_u\)。
- 对于 \(t = 3\),修复 \(u \to v\)。\(S\) 增加 \(w_u\),\(a_v\) 减少 \(w_u\)。
- 对于 \(t = 4\),修复所有以 \(u\) 为终点的边。\(S\) 增加 \(a_u\),\(a_u\) 变成 \(0\)。
时间复杂度为 \(O(n)\)。
#include <bits/stdc++.h>
typedef unsigned long long ULL;
const int N = 500005;
ULL w[N], a[N], s[N];
std::mt19937_64 rng(time(0));
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, q;
std::cin >> n >> m;
ULL target = 0;
for (int i = 1; i <= n; i++) {
w[i] = rng();
target += w[i];
}
ULL res = 0;
for (int i = 1; i <= m; i++) {
int u, v;
std::cin >> u >> v;
res += w[u];
s[v] += w[u];
}
std::cin >> q;
for (int i = 1; i <= q; i++) {
int opt, u, v;
std::cin >> opt >> u;
if (opt == 1) {
std::cin >> v;
res -= w[u];
a[v] += w[u];
} else if (opt == 2) {
res -= s[u] - a[u];
a[u] = s[u];
} else if (opt == 3) {
std::cin >> v;
res += w[u];
a[v] -= w[u];
} else {
res += a[u];
a[u] = 0;
}
if (res == target) {
std::cout << "YES" << '\n';
} else {
std::cout << "NO" << '\n';
}
}
return 0;
}

浙公网安备 33010602011771号