P8819 [CSP-S 2022] 星战

P8819 [CSP-S 2022] 星战

前言(废话)

总司令题对我的影响真是太大了,有太多故事。

所以在做之前写了 \(1h+\) 的闲话,无法静下心来。

那么闲话写得差不多了,可以开始认真做题了吧。

我之前居然尝试做过总司令欸,因为多篇题解被我点赞了。

题意

给你一个有向图。

\(4\) 种操作:

  1. 摧毁边 \((u,v)\)
  2. 摧毁点 \(u\) 所有入边。
  3. 修复 \((u,v)\)
  4. 修复点 \(u\) 所有入边。

每次操作后,告诉总司令能不能发起总攻。

如果满足所有点出发都可以到达环,且每个点出度 \(\le 1\),则可以发起总攻。

\(n,m,q \le 5 \times 10^5\)

思路

折叠的错误分析

分析一下什么情况下能发起总攻。

当图是一个内向基环树森林的时候,可以发起总攻。

所以我们要维护操作,判断当前是否是一个内向基环树森林。


\(O(q(n+m))\) 的做法是显然的。

显然总边数不等于 \(n\) 的时候不能发起总攻。

好神秘啊,怎么做啊。

当总边数第一次等于 \(n\) 的时候,即此时是一个基环数森林。我们 \(O(n)\) 跑一遍,找到每棵树的环,以及每条边的方向是否正确。

不会做,看题解吧。

啊呀呀,我的分析不够。

分析什么时候能发起总攻。

首先每个点的出度恰好为 \(1\)

这样每个点必定都能到达环。

所以问题变成维护每个点的出度是否都恰好为 \(1\)


操作 \(2,4\) 不好维护。

我们只会 \(O(1)\) 维护每个点的入度。无法快速维护出度。

入度和等于出度和等于 \(n\)。这是一个必要条件。

每个点出度为 \(1\),相当于每个点只能给恰好一个点提供入度。

应该是顺着这个思路,想到一个和哈希的做法。

每个点随机一个权值 \(w_u\)

\(f_u = \sum_{v \in {in_u}} w_v\)

我们可以单次 \(O(1)\) 维护点 \(u\) 所有入边的权值和 \(f_u\)

\(\sum f_u = \sum w_u\),那么大概率每个点出度为 \(1\)

好牛的哈希做法!很震撼!

做完了,时间复杂度线性。

code

这题除了数据实在是太水之外,还是很有意思并且很友好的。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
	typedef unsigned int uint;
	constexpr int N=5e5+7,mod=1e9+9;
	int n,m,q;
	mt19937 rd(random_device{}());
	uint w[N];
	struct moduint {
		uint x;
		moduint (uint y=0) : x(y) {} 
		bool operator == (moduint b) { return x == b.x; }
		moduint operator + (moduint b) const { return x+b.x<mod ? x+b.x : x+b.x-mod; }
		moduint operator - (moduint b) const { return x+mod-b.x<mod ? x+mod-b.x : x-b.x; }
		moduint &operator += (moduint b) { return *this = *this + b; }
		moduint &operator -= (moduint b) { return *this = *this - b; }
	};
	moduint s[N],p[N];
	void main() {
		sf("%d%d",&n,&m);
		rep(i,1,n) w[i] = rd()%mod, s[0] += w[i];
		rep(i,1,m) {
			int u,v;
			sf("%d%d",&u,&v);
			s[v] += w[u];
		}
		rep(i,1,n) p[i] = s[i], p[0] += p[i];
		sf("%d",&q);
		while(q--) {
			int t;
			int u,v;
			sf("%d",&t);
			if(t==1) {
				sf("%d%d",&u,&v);
				p[v] -= w[u];
				p[0] -= w[u];
			} else if(t==2) {
				sf("%d",&v);
				p[0] -= p[v];
				p[v] = 0;
			} else if(t==3) {
				sf("%d%d",&u,&v);
				p[v] += w[u];
				p[0] += w[u];
			} else {
				sf("%d",&v);
				p[0] -= p[v];
				p[v] = s[v];
				p[0] += p[v];
			}
			if(p[0] == s[0]) puts("YES");
			else puts("NO");
		}
	}
}
int main() {
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("my.out","w",stdout);
	#endif
	wing_heart :: main();
}
posted @ 2025-09-29 10:58  wing_heart  阅读(26)  评论(0)    收藏  举报