Codeforces Global Round 23 F. Kazaee(随机化哈希+线段树)

题目

长度为n(n<=3e5)的数组,q(q<=3e5)次操作,操作分两种,

1. 输入i x(x<=1e9),将ai改成x

2. 输入l r k(1<=k<=n),询问对于在[l,r]内出现的每种数,其在[l,r]内的出现次数是不是均为k的倍数

思路来源

gzchenben的讲解

jjleo的代码

题解

随机化哈希,人尽皆知典中典

k的倍数,区间每种数的出现次数很难维护,考虑概率&哈希

对于输入的ai,先将其哈希映射到另一个数上,然后考虑按二进制位拆,

如果对于二进制的每一位,其在[l,r]内的和都是k的倍数,我们就认为答案是Yes,

这样错误的概率,对于某一位来说,在k=2时,是1/2,

因为随机哈希,可能将两个二进制这一位原本不同的数,映射成相同了,

但是,控制位数足够多后,这样询问出错的概率就很低了,

考虑到3e5组样例,实际后台可能有100个case,3e8左右,所以30位≈1e9不一定行,

这里控制位数M在35-40左右即可

不被hack掉的tips

1. 哈希是完全随机的(如mt19937_64)

2. 对于不同的ai,哈希出来的数不重复

心得

mt19937_64用起来很方便,实际控制M=64保险,但M=35-40就够用了,1/(2^35)的概率很低了

放一个抄来的随机数生成的方法,主要这题需要尽可能保证每次gen的随机数不重复

update:unordered_map被卡了,赶紧改回map

int gen(){
	static int o=4354347;
	o=(1ll*o*19260817+23333333)%998244353+1;
	return o;
}

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10,M=40;
int n,q,a[N],op,x,y,l,r,k;
map<int,ll>to;
std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
ll gen(){
	return rng()&((1ll<<40)-1);
}
struct BIT{
	int n,tr[N];
	void init(int _n){
		n=_n;
		memset(tr,0,sizeof tr);
	}
	void add(int x,int v){
		for(int i=x;i<=n;i+=i&-i)
		tr[i]+=v;
	}
	int sum(int x){
		int ans=0; 
		for(int i=x;i;i-=i&-i)
		ans+=tr[i];
		return ans;
	}
}tr[M];
int main(){
	scanf("%d%d",&n,&q);
	for(int j=0;j<M;++j){
		tr[j].init(n);
	}
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		if(!to.count(a[i])){
			to[a[i]]=gen();
		}
		int v=to[a[i]];
		for(int j=0;j<M;++j){
			if(v>>j&1)tr[j].add(i,1);
		}
	}
	for(int i=1;i<=q;++i){
		scanf("%d",&op);
		if(op==1){
			scanf("%d%d",&x,&y);
			if(!to.count(y)){
				to[y]=gen();
			}
			int p=to[a[x]],v=to[y];
			for(int j=0;j<M;++j){
				int w=(v>>j&1)-(p>>j&1);
				if(w)tr[j].add(x,w);
			}
			a[x]=y;
		}
		else{
			scanf("%d%d%d",&l,&r,&k);
			bool ok=1;
			for(int j=0;j<M && ok;++j){
				ok&=((tr[j].sum(r)-tr[j].sum(l-1))%k==0);
			}
			puts(ok?"YES":"NO");
		}

	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值