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;
}
posted @ 2024-10-04 18:32  Unino  阅读(21)  评论(0)    收藏  举报