星战

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;
}

这种做法的名字:和哈希

posted @ 2024-07-12 15:57  wscqwq  阅读(1)  评论(0编辑  收藏  举报