A49999 正经题解
2025-11-15 22:24:25
发布于:广东
20阅读
0回复
0点赞
题意分析
我们有一个有向图,n个点,m条有向边(每个点的“虫洞”指的是以该点为终点的边)。
敌人打击有两种:
摧毁某条虫洞——只是让这条边不可用,但边仍然存在。
摧毁某个据点——会同时摧毁所有以该点为终点的边(即该点的入边),但不会影响该点出发的边。
我方修复有两种:
修复某条虫洞
修复某个据点—— 修复该点所有被摧毁的入边。
反攻条件:
反击:从任意据点出发,可以无限次虫洞穿梭(即该点在环中或能到达环)。
连续穿梭:当且仅当只有一个从该点出发的虫洞可用时,才能连续穿梭(即每个点的出度 = 1)。
所有据点都满足上述两个条件时,才能反攻。
思路分析
小时候做这题看到数据范围吓哭了,这意味着我们实现的算法只可能是,因此必须着手考虑优化
反攻需要整个图弱连通且个节点点出度都为,即整个图是一个有向且只有个顶点的环(我记得叫单环功能图?),直接按照图论问题思考不太可行……
那我把图论问题转换一下再做不就是了,定义为号节点的哈希权值,为初始状态下,号节点所有入边起点的权值和,为当前状态下,号节点所有有效入边起点的权值和,为,为。由于使用的是来随机,所以在范围内哈希冲突的概率极小,基本可以忽略。
当图满足反攻条件时,每个点出度为,整个图是一个大环(弱连通),那么每个节点的权值会通过它的出边传递给下一个节点,由于是完整的大环,每个权值都会在环中传递一圈,最终(所有节点入边权值和)正好等于(所有权值总和)。反之,如果图不满足条件(出度不为1或不连通),就不会等于。
因此我们推出:当时,可以反攻。
对于每个操作,按题目与数组表意修改即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
const int N = 500005;
ull a[N], val[N], f[N], sum, cnt;
int n, m, q;
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
mt19937_64 rng(time(0));
cin >> n >> m;
for(int i=1;i<=n;i++){
a[i] = rng();
sum += a[i];
}
for(int i=1;i<=m;i++){
int u, v;
cin >> u >> v;
val[v] += a[u], f[v] += a[u];
}
for(int i=1;i<=n;i++){
cnt += f[i];
}
cin >> q;
while(q--){
int op, x, y;
cin >> op;
if(op==1){
cin >> x >> y;
f[y] -= a[x];
cnt -= a[x];
}else if(op==2){
cin >> x;
cnt -= f[x];
f[x] = 0;
}else if(op==3){
cin >> x >> y;
f[y] += a[x];
cnt += a[x];
}else if(op==4){
cin >> x;
cnt += val[x]-f[x];
f[x] = val[x];
}
if(cnt==sum)
cout << "YES" << '\n';
else
cout << "NO" << '\n';
}
return 0;
}
时间复杂度
,可以处理的较大数据。
这里空空如也





有帮助,赞一个