NOI2024 集合 题解

给个链接:集合

很神秘的题目。基本上看到之后就可以想到哈希。

首先想到一个比较神秘的暴力。就是对于每个询问,扫一遍所有 a 中的数出现的位置,把它弄成一个哈希值(具体怎么弄随意)存到 set 里,然后看看是不是和 b 中的数出现的位置这样操作后的集合完全相等。事实上就是判断是否对于所有在 a 中这个区间内出现的数 x,都存在一个在 b 中出现的不同的数 y,使得 x,y 出现的位置完全相同。这样做应该是有 70 分的,但是显然不够。

然后我们考虑一个事情,对于每一个 i[i,j] 这个区间合法在 j 尽可能小的时候最有可能成立。换句话说有单调性,可以二分。

但是,二分是没有必要的,我们可以用双指针做的更好。因为,如果 [i,j] 合法,[i+1,j] 存在,那么 [i+1,j] 合法。

所以我们对每个 i 找出最靠右的 j 且满足 [i,j] 合法,然后就可以 O(1) 回答询问。时间复杂度 O(n+q)

这里为了保险,把每个位置的值也做了哈希,用 pi 存储。

给个代码:

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define N 600005
using namespace std;
int n,m,q,a[N][3],b[N][3];
ull nowa[N],nowb[N],suma,sumb,p[N];
int ri[N];//使[i,j]合法的最靠右的j
ull get_rnd(ull x){
	return x*x*x;//随便变换一下
}
void work(int id,int type){
	for(int j=0;j<3;j++){
		ull i=a[id][j];
		suma-=get_rnd(nowa[i]);//把原来的值减掉
		nowa[i]+=type*p[id];//看情况加上或减去这个位置的哈希值
		suma+=get_rnd(nowa[i]);//加上现在的值
	}
	for(int j=0;j<3;j++){//这里同理
		ull i=b[id][j];
		sumb-=get_rnd(nowb[i]);
		nowb[i]+=type*p[id];
		sumb+=get_rnd(nowb[i]);
	}
}
signed main(){
	srand(time(0));
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		for(int j=0;j<3;j++){
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<3;j++){
			cin>>b[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		ri[i]=n;
	}
	for(int i=1;i<=n;i++){
		p[i]=rand()*rand()+923;
	}
	work(1,1);
	for(int i=1,j=1;i<=n;i++){
		while(j<=n&&suma==sumb){//如果当前仍然合法
			j++;
			if(j>n)break;
			work(j,1);
		}
		ri[i]=min(ri[i],j-1);
		work(i,-1);//i要右移,所以撤掉这一位的贡献
	}
	while(q--){
		int l,r;
		cin>>l>>r;
		cout<<(ri[l]>=r?"Yes\n":"No\n");
	}
	return 0;
}
posted @   zxh923  阅读(124)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示