Preface

不可以,总司令 的来源.

Description

给定一张 n n n m m m 边的有向图 G G G,有 q q q 次操作分四种:

  • 1 u v:使边 u → v u\to v uv 失活.
  • 2 u:使点 u u u 的所有入边失活.
  • 3 u v:使边 u → v u\to v uv 激活.
  • 4 u:使点 u u u 的所有入边激活.

每次操作后,输出 G G G 中所有激活的边是否构成基环内向森林.

Limitations

1 ≤ n , m , q ≤ 5 × 1 0 5 1\le n,m,q \le 5\times 10^5 1n,m,q5×105
1 ≤ u , v ≤ n 1 \le u,v \le n 1u,vn
2 s , 512 MB 2\text{s},512\text{MB} 2s,512MB

Solution

设所有边的起点构成可重集 S S S,那么问题可转化为判断 S = { 1 , 2 , ⋯   , n } S=\{1,2,\cdots,n\} S={1,2,,n}.
为了快速修改 S S S,我们维护以下集合:

  • g u g_u gu初始图中, u u u 的所有入边的起点的可重集.
  • h u h_u hu当前图中, u u u 的所有入边的起点的可重集.

然后考虑每个操作,显然有:

  • 1 u v:执行 h v ← h v − { u } h_v\gets h_v-\{u\} hvhv{u} S ← S − { u } S\gets S-\{u\} SS{u}.
  • 2 u:执行 S ← h u S\gets h_u Shu h u ← ∅ h_u\gets\varnothing hu.
  • 3 u v:操作 1 1 1 反过来.
  • 4 u:执行 S ← S ∪ ( g u − h u ) S\gets S\cup(g_u-h_u) SS(guhu) h u = g u h_u=g_u hu=gu.

接下来就很显然了,给每个点一个随机哈希值 w i w_i wi,用 w i w_i wi 相加来表示上述集合即可.(这种方法被称作 和哈希

Code

1.68 KB , 11.78 s , 11.55 MB    (in   total,   C++20   with   O2) 1.68\text{KB},11.78\text{s},11.55\text{MB}\;\texttt{(in total, C++20 with O2)} 1.68KB,11.78s,11.55MB(in total, C++20 with O2)
使用 mt19937 生成 w i w_i wi.

// Problem: P8819 [CSP-S 2022] 星战
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8819
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}


signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m;
	cin >> n >> m;
	
	mt19937 rng(time(0));
	vector<i64> h(n);
	i64 ans = 0, tot = 0;
	
	for (int i = 0; i < n; i++) ans += (h[i] = rng());
	
	vector<i64> to(n, 0), sum(n, 0);
	for (int i = 0, u, v; i < m; i++) {
	    cin >> u >> v;
	    u--, v--;
	    
	    to[v] += h[u];
	    sum[v] = to[v];
	    tot += h[u];
	}
	
	int q;
    cin >> q;
    
    for (int i = 0, t, u, v; i < q; i++) {
        cin >> t >> u; u--;
        
        if (t == 1) {
            cin >> v; v--;
            to[v] -= h[u];
            tot -= h[u];
        }
        
        if (t == 2) {
            tot -= to[u];
            to[u] = 0;
        }
        
        if (t == 3) {
            cin >> v; v--;
            to[v] += h[u];
            tot += h[u];
        }
        
        if (t == 4) {
            tot += sum[u] - to[u];
            to[u] = sum[u];
        }
        
        cout << (tot == ans? "YES": "NO") << endl;
    }
	
	return 0;
}

Extra

人尽皆知的一点:全输出 NO 45    pts 45\;\texttt{pts} 45pts.
用插件修改了提交记录显示,还挺好看的.

Logo

集算法之大成!助力oier实现梦想!

更多推荐