P8819 星战 Sol

挺 nb 的一道题。

考场没想到,大失败。

会的都会,不会的怎么都不会了属于是。

首先看到每一个点只能恰好连一条边,也就是整张图内点的出度均为 \(1\)

则最后状态为 \(n\) 个点、\(n\) 条边。

这是整张图为基环树森林的充分条件。

因为出度只能为 \(1\),想到随机赋点权来判断整张图是否为基环树森林。

如果不为基环树森林,那么直接输出 NO。

接着,我们就保证了整张图的每一个点出度为 \(1\),且其为基环树森林。

此时可以想一想,每一个子连通块内可以变为一个环和连在上面的一堆链。

考虑到出度必须为 \(1\),这些链只能一致指向环的某个节点。

那么这个连通块内的每一个点都能到达一个环。

换句话说,第二个条件是第一个条件的充分条件。

即“点的出度均为 \(1\)\(\rightarrow\) “可以到达环”。

没了。第一个条件是诈骗。

随机权值,维护两个数组,一个表示当前剩余的权值,一个表示当前丢失的权值。

注意 \(u \rightarrow v\) 的贡献 \(val_u\) 要记到 \(v\) 头上。

#include <bits/stdc++.h>
using ull = unsigned long long;
using namespace std;

const int N = 5e5 + 10;
int n, m, q; mt19937_64 rnd(998244853);
ull stnd, tmp, val[N], sum[N], _sum[N];

inline void solve() {
	int op, u, v; scanf("%d%d", &op, &u);
	if (op == 1) scanf("%d", &v), tmp -= val[u], sum[v] -= val[u], _sum[v] += val[u];
	else if (op == 2) tmp -= sum[u], _sum[u] += sum[u], sum[u] = 0;
	else if (op == 3) scanf("%d", &v), tmp += val[u], sum[v] += val[u], _sum[v] -= val[u];
	else tmp += _sum[u], sum[u] += _sum[u], _sum[u] = 0;
	puts(tmp == stnd? "YES" : "NO"); return ;
}

int main() {
	scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) stnd += (val[i] = rnd());
	for (int i = 1, u, v; i <= m; ++i) scanf("%d%d", &u, &v), sum[v] += val[u], tmp += val[u];
	scanf("%d", &q); while (q--) solve();
	return 0;
}
posted @ 2022-10-30 22:52  MistZero  阅读(35)  评论(0编辑  收藏  举报