P8819 [CSP-S 2022] 星战
P8819 [CSP-S 2022] 星战
前言(废话)
总司令题对我的影响真是太大了,有太多故事。
所以在做之前写了 \(1h+\) 的闲话,无法静下心来。
那么闲话写得差不多了,可以开始认真做题了吧。
我之前居然尝试做过总司令欸,因为多篇题解被我点赞了。
题意
给你一个有向图。
有 \(4\) 种操作:
- 摧毁边 \((u,v)\)。
- 摧毁点 \(u\) 所有入边。
- 修复 \((u,v)\)。
- 修复点 \(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();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/19118159

浙公网安备 33010602011771号