随机化的题都是好题。
考场遇上随机化是真的搞心态,何况正解是用随机化通过的题本蒻至今也只在考场上遇见过两道。。。
闲言少叙,书归正传。
题目是在初始时给定一张图,然后在图上进行下列四种操作:
- 将 u , v u,v u,v 之间的边断开
- 将 u , v u,v u,v 之间的边重连
- 将 v v v 的所有入边断开
- 将 v v v 的所有入边重连
并且保证断开和重连的总是原图中的边,每次操作后询问是否满足:
- 所有点的出度均为 1 1 1
- 所有边都能通往或属于一个环
仔细思考,会发现其实当第一个条件满足时,第二个条件会自动满足。那么我们判断是否有:所有点的出度均为 1 1 1,即可。
考虑当所有点的出度均为 1 1 1 时,整张图的出度和为 n n n,所以判断出度和是否为 n n n 即可。但是这么做会出现问题,即 n = a + b = ( a + 1 ) + ( b − 1 ) n=a+b=(a+1)+(b-1) n=a+b=(a+1)+(b−1)。
解决方案也很简单,我们给每个点一个尽可能不相同的权值 w i w_i wi,然后检查每个点的出度与其权值乘积的和是否等于 ∑ i = 1 n w i \sum_{i=1}^nw_i ∑i=1nwi 即可。这实际上相当于 Hash,权值可以直接用随机数指定(亲测令 w i = i + 1 w_i=i+1 wi=i+1 也能通过)。
这里给出一个用随机数的代码,时间复杂度 Θ ( n + m + q ) \Theta(n+m+q) Θ(n+m+q)。
#include<bits/stdc++.h>
using namespace std;
//星战
const int maxn = (int)5e5 + 5;
long long allin[maxn];
long long inv[maxn];
long long tot, YES;
int w[maxn];
int n, m, q;
int main() {
srand(time(0));
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) w[i] = rand();
for (int i = 1; i <= n; i++) YES += w[i];
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d %d", &u, &v);
allin[v] += w[u];
tot += w[u];
}
for (int i = 1; i <= n; i++) inv[i] = allin[i];
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
int t, u, v;
scanf("%d", &t);
if (t == 1) {
scanf("%d %d", &u, &v);
inv[v] -= w[u];
tot -= w[u];
}
if (t == 2) {
scanf("%d", &v);
tot -= inv[v];
inv[v] = 0;
}
if (t == 3) {
scanf("%d %d", &u, &v);
inv[v] += w[u];
tot += w[u];
}
if (t == 4) {
scanf("%d", &v);
tot -= inv[v];
tot += allin[v];
inv[v] = allin[v];
}
if (tot == YES) printf("YES\n");
else printf("NO\n");
}
return 0;
}