https://www.luogu.com.cn/problem/P8819
https://www.acwing.com/problem/content/description/4737/
- 为了使虫洞穿梭的过程连续,尽量减少战舰在据点切换虫洞时的质能损耗,当且仅当只有一个从该据点出发的虫洞可用时,这个据点可以实现连续穿梭。
- 如果我方所有据点都可以实现连续穿梭,那么每个点都可以一直走,也能满足我方所有据点都可以实现反击,所以只需要判断每个点的出度是否都是 \(1\) 即可。
我们把终点为据点 \(u\) 的所有虫洞归为据点 \(u\) 的虫洞。
敌人会摧毁某个虫洞,这会使它连接的两个据点无法再通过这个虫洞直接到达,但这样的打击无法摧毁它连接的两个据点。
B 型特种部队可以将某据点的所有损坏的虫洞修复。
- 若 \(t = 2\),接下来一个整数 \(u\) 表示敌人摧毁了据点 \(u\)。如果该据点的虫洞已全部被摧毁,那么这次袭击不会有任何效果。
- 若 \(t = 4\),接下来一个整数 \(u\) 表示我方修复了据点 \(u\)。如果该据点没有被摧毁的虫洞,那么这次修复不会有任何效果。
题目的暗示:记录每个点的出度,可以分别记录以点 \(u\) 为终点的虫洞的起始点有哪些点,用一个01数组记录,若有 \(i\rightarrow u\) 的边,则 \(s_u\) 的第 \(i\) 位=1。然后最后求 \(\sum_{u=1}^n s_u\) 是否恰好每一位都是1即可。
如果按照 \(n\) 位维护,遇到2、4操作,无论如何都是 \(O(n)\),最坏达到 \(O(nq)\),TLE。
由CCF的特性以及往年无解的分数可知,正确率高的算法也可以通过此题。
我们发现这个东西很像字符串哈希,将一个需要 \(O(n)\) 比较的str转换成num,只是需要支持相加操作。
其实每一位赋值不同的权值,发现 \(h(s_x)+h(s_y)=h(s_x+s_y)\),因为每一位是可以单独考虑的。
我们将 \(s_u\) 转换成了哈希值了。
每次只需要将涉及到的那个 \(s_u\) 先减去,遇到1、3更新一位,遇到2、4直接置空、恢复初始状态,然后加上新的值即可。
用 ull 可以自动取模。
复杂度 \(O(n+m+q)\)。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=500010,P=13331;
int n,m,q;
ull p[N],ao,res,org[N],cur[N];
int main(){
#ifndef ONLINE_JUDGE
freopen("1.txt","r",stdin);
#endif
#ifdef ONLINE_JUDGE
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
#endif
p[0]=1;
cin>>n>>m;
for(int i=1;i<=n;++i)p[i]=p[i-1]*P;
for(int i=1;i<=m;++i){
int a,b;
cin>>a>>b;
org[b]+=p[a],cur[b]+=p[a];
}
for(int i=1;i<=n;++i)ao+=p[i],res+=cur[i];
cin>>q;
for(int i=1;i<=q;++i){
int t,u,v=0;
cin>>t>>u;
if(t==1||t==3)cin>>v;
res-=cur[u]+cur[v];
if(t==1)cur[v]-=p[u];
if(t==2)cur[u]=0;
if(t==3)cur[v]+=p[u];
if(t==4)cur[u]=org[u];
res+=cur[u]+cur[v];
cout<<(ao==res?"YES\n":"NO\n");
}
return 0;
}