P8819 [CSP-S 2022] 星战 Solution
不可以,总司令的来源.
Preface
不可以,总司令
的来源.
Description
给定一张 n n n 点 m m m 边的有向图 G G G,有 q q q 次操作分四种:
1 u v
:使边 u → v u\to v u→v 失活.2 u
:使点 u u u 的所有入边失活.3 u v
:使边 u → v u\to v u→v 激活.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 1≤n,m,q≤5×105
1 ≤ u , v ≤ n 1 \le u,v \le n 1≤u,v≤n
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\} hv←hv−{u}, S ← S − { u } S\gets S-\{u\} S←S−{u}.2 u
:执行 S ← S − h u S\gets S-h_u S←S−hu, 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) S←S∪(gu−hu), h u ← g u h_u\gets 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.
用插件修改了提交记录显示,还挺好看的.
更多推荐
所有评论(0)