Preface
不可以,总司令
的来源.
Description
给定一张 nnn 点 mmm 边的有向图 GGG,有 qqq 次操作分四种:
1 u v
:使边 u→vu\to vu→v 失活.2 u
:使点 uuu 的所有入边失活.3 u v
:使边 u→vu\to vu→v 激活.4 u
:使点 uuu 的所有入边激活.
每次操作后,输出 GGG 中所有激活的边是否构成基环内向森林.
Limitations
1≤n,m,q≤5×1051\le n,m,q \le 5\times 10^51≤n,m,q≤5×105
1≤u,v≤n1 \le u,v \le n1≤u,v≤n
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\}hv←hv−{u},S←S−{u}S\gets S-\{u\}S←S−{u}.2 u
:执行 S←S−huS\gets S-h_uS←S−hu,hu←∅h_u\gets\varnothinghu←∅.3 u v
:操作 111 反过来.4 u
:执行 S←S∪(gu−hu)S\gets S\cup(g_u-h_u)S←S∪(gu−hu),hu←guh_u\gets g_uhu←gu.
接下来就很显然了,给每个点一个随机哈希值 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
人尽皆知的一点:全输出 NO
有 45 pts45\;\texttt{pts}45pts.
用插件修改了提交记录显示,还挺好看的.