CF GR23 F. Kazaee

题目链接

随机大法妙啊!

原本想考虑每种数的贡献,在不合法的区间做标记;但这不仅麻烦,而且要以k为循环节考虑,不可做。

而对于每次询问,又不能每个数都检验一遍;同时考虑如果只检验区间长度(即所有数出现次数之和)是可能把no判断为yes的。

但发现答案为yes要求每种数都合法,只要有任何一个数(或一个集合的数的出现次数和)不合法都不行;而要检验一个集合,通过预处理也只需O(1)。

考虑用随机算法把错判的概率降到可接受的范围,以减少检验次数:

考虑构造一次检测,使得把no判断为yes的概率尽量小:即本来存在至少一个不合法的数(合法的数不用考虑),要在这个不合法的数的集合中随机出一个子集,直接每个数等概率取或不取,即可把概率降到1/2之内(k=2时是1/2,k更大时,由直觉可知概率不会更大)。然后做多次这样的随机,因为每次的随机都是独立的,故最终错判的概率就很小了。对于多测,由于单个错判的概率很小,用无穷小量的思想可以算出总错判概率约等于×

因为要随机的数较多,用普通的rand会wa,要用 mt19937 myrand(time(0));以生成质量更高的随机数(范围为unsigned int)。

#include<bits/stdc++.h>
using namespace std;
mt19937 rnd(time(0));
const int N=6e5+5,M=30;
int n,m,q,a[N],b[N];
struct query{
	int op,x,y,z;
}p[N];
map<int,int>mp;
void idx(int& x){
	if(!mp[x]) mp[x]=++m;
	x=mp[x];
}
int s[M][N];
void add(int u,int x,int y){
	while(x<=n) s[u][x]+=y,x+=x&-x;
}
int cnt(int u,int x){
	int su=0;
	while(x) su+=s[u][x],x-=x&-x;
	return su;
}
vector<int>id[N];
void work(int d,int x,int y){
	int z=id[x].size();
	for(int i=0;i<z;i++) add(id[x][i],d,y);
}
int main()
{
	//srand(time(0));
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	//int T; cin>>T; while(T--) work();
	cin>>n>>q;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),idx(a[i]);
	for(int i=1;i<=q;i++){
		scanf("%d %d %d",&p[i].op,&p[i].x,&p[i].y);
		if(p[i].op==2) scanf("%d",&p[i].z);
		else idx(p[i].y);
	}
	for(int i=0;i<M;i++){
		for(int j=1;j<=m;j++){
			//srand(time(0));
			int x=rnd();
			if(x&1) id[j].push_back(i);
		}
	}
	for(int i=1;i<=n;i++) work(i,a[i],1);
	for(int i=1;i<=q;i++){
		if(p[i].op==1){
			work(p[i].x,a[p[i].x],-1);
			a[p[i].x]=p[i].y;
			//idx(a[p[i].x]);
			work(p[i].x,a[p[i].x],1);
		}
		else{
			bool pd=0;
			for(int j=0;j<M;j++) if((cnt(j,p[i].y)-cnt(j,p[i].x-1))%p[i].z){
				pd=1;
				break;
			}
			if(pd) puts("NO");
			else puts("YES");
		}
	}
	return 0;
}

随机函数讲解

posted @   sz[sz]  阅读(70)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示