P8819 [CSP-S 2022] 星战 Solution

Preface

不可以,总司令 的来源.

Description

给定一张 nnnmmm 边的有向图 GGG,有 qqq 次操作分四种:

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

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

Limitations

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

Solution

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

  • gug_ugu初始图中,uuu 的所有入边的起点的可重集.
  • huh_uhu当前图中,uuu 的所有入边的起点的可重集.

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

  • 1 u v:执行 hv←hv−{u}h_v\gets h_v-\{u\}hvhv{u}S←S−{u}S\gets S-\{u\}SS{u}.
  • 2 u:执行 S←S−huS\gets S-h_uSShuhu←∅h_u\gets\varnothinghu.
  • 3 u v:操作 111 反过来.
  • 4 u:执行 S←S∪(gu−hu)S\gets S\cup(g_u-h_u)SS(guhu)hu←guh_u\gets g_uhugu.

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

Code

1.68KB,11.78s,11.55MB  (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 生成 wiw_iwi.

// 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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值