随机化的题都是好题。
考场遇上随机化是真的搞心态,何况正解是用随机化通过的题本蒻至今也只在考场上遇见过两道。。。
闲言少叙,书归正传。
题目是在初始时给定一张图,然后在图上进行下列四种操作:
- 将 u,vu,vu,v 之间的边断开
- 将 u,vu,vu,v 之间的边重连
- 将 vvv 的所有入边断开
- 将 vvv 的所有入边重连
并且保证断开和重连的总是原图中的边,每次操作后询问是否满足:
- 所有点的出度均为 111
- 所有边都能通往或属于一个环
仔细思考,会发现其实当第一个条件满足时,第二个条件会自动满足。那么我们判断是否有:所有点的出度均为 111,即可。
考虑当所有点的出度均为 111 时,整张图的出度和为 nnn,所以判断出度和是否为 nnn 即可。但是这么做会出现问题,即 n=a+b=(a+1)+(b−1)n=a+b=(a+1)+(b-1)n=a+b=(a+1)+(b−1)。
解决方案也很简单,我们给每个点一个尽可能不相同的权值 wiw_iwi,然后检查每个点的出度与其权值乘积的和是否等于 ∑i=1nwi\sum_{i=1}^nw_i∑i=1nwi 即可。这实际上相当于 Hash,权值可以直接用随机数指定(亲测令 wi=i+1w_i=i+1wi=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;
}
本文介绍了一种使用随机化算法解决特定图更新问题的方法。该问题要求在一系列操作后判断图中所有节点的出度是否均为1,并且所有边能够形成环。通过为每个节点分配一个独特的权重并利用这些权重进行哈希检查,可以高效地解决这个问题。
579

被折叠的 条评论
为什么被折叠?



