CF1746F

Kazaee

题面翻译

题目描述

给出一个长度为 \(n\) 的数组 \(a\) 和以下两种操作:

  • \(1\ i\ x\):将 \(a_i\) 修改为 \(x\)
  • \(2\ l\ r\ k\):询问在数组区间 \([l, r]\) 内是否每个出现过的正整数的出现次数都是 \(k\) 的倍数。(建议参照样例理解)若是则输出 YES,若否则输出 NO

输入格式

第一行两个整数 \(n\)\(q\),表示数组长度和操作数。

第二行 \(n\) 个整数,为数组 \(a\) 中的元素。(下标从1开始)

之后 \(q\) 行,每行一个询问。

输出格式

对于每个操作2,给出相应答案(YES 或 NO)。

样例 #1

样例输入 #1

10 8
1234 2 3 3 2 1 1 2 3 4
2 1 6 2
1 1 1
2 1 6 2
2 1 9 2
1 10 5
2 1 9 3
1 3 5
2 3 10 2

样例输出 #1

NO
YES
NO
YES
YES
神仙题!!!
tag 是树状数组+HASH
其实我也想到了思路:
很容易想到出现次数是k的倍数的必要条件是 区间和是k的倍数
但是若是初始序列单一确定的值很容易构造出反例
所以我们用随机化的思想 
在保证相同的数赋值随机值相同的情况下多次随机
若都满足 那就期望正确
具体代码的实现:先对操作离线处理  存入所有的值后去重找位置记录下来 就构成了多个相同数的集合 然后随机化30次左右就行了
点击查看代码
#include<bits/stdc++.h>
#include<ctime>
using namespace std;
#define int long long
mt19937 rnd(time(NULL));
const int N=300005;
const int mod=1e9+7;
int a[N+5],tr[N+5],n,q,m,b[(N<<1)+5],op[N+5],pos[N+5],x[N+5],l[N+5],r[N+5],k[N+5],ans[N+5],val[2*N+5],pre[N+5];//离线操作 
int lowbit(int x)
{
	return x&(-x);
}
void add(int n,int p,int x)
{
	for(;p<=n;p+=lowbit(p))
		tr[p]+=x;
}
int query(int p)
{
	int sum=0;
	for(;p;p-=lowbit(p))
		sum+=tr[p];
	return sum;
}
signed main()//对相同的数赋予相同的随机值
{
	cin>>n>>q;
	srand(time(NULL));
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		b[++m]=a[i];
	}
	for(int i=1;i<=q;i++)//对操作离线处理
	{
		scanf("%lld",&op[i]);
		if(op[i]==1)
		{
			scanf("%lld",&pos[i]);
			scanf("%lld",&x[i]);
			b[++m]=x[i];
		}
		else
		{
			scanf("%lld%lld%lld",&l[i],&r[i],&k[i]);
			if((r[i]-l[i]+1)%k[i]==0)ans[i]=1;
		}
	}
	sort(b+1,b+m+1);
	m=unique(b+1,b+m+1)-(b+1);//去重前若两个不同位置的数相等那么去重后位置就相同
	for(int i=1;i<=n;i++)
		a[i]=lower_bound(b+1,b+m+1,a[i])-b;
	for(int i=1;i<=q;i++)
		if(op[i]==1)
			x[i]=lower_bound(b+1,b+m+1,x[i])-b;
	for(int kk=1;kk<=30;kk++)
	{
		memset(tr,0,sizeof(tr));
		for(int i=1;i<=m;i++)val[i]=rnd()%mod;
		for(int i=1;i<=n;i++)
		{
			pre[i]=a[i];
			add(n,i,val[a[i]]);
		}
		for(int i=1;i<=q;i++)
		{
			if(op[i]==1)
			{
				add(n,pos[i],val[x[i]]-val[pre[pos[i]]]);//点修改 要算出差值
				pre[pos[i]]=x[i];	
			} 
			else
			{
				ans[i]&=(query(r[i])-query(l[i]-1))%k[i]==0;
			}
		}
	}
	for(int i=1;i<=q;i++)
	{
		if(op[i]==2)
		{
			if(ans[i])printf("YES\n");
			else printf("NO\n"); 
		}
	}
	return 0;
} 
posted @ 2023-01-11 12:29  PKU_IMCOMING  阅读(12)  评论(0)    收藏  举报