
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 ← h u S\gets h_u 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=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)