【BZOJ2821】作诗(Poetize)—(分块)

传送门

还算好的一道分块题了吧

除了细节问题炸锅1h++以外

考虑要询问出现偶数次的数的个数

我们可以 n n n\sqrt{n} nn 预处理出两个数组:

n u m [ i ] [ j ] num[i][j] num[i][j]表示 j j j这个数在前 i i i块中出现了多少次

这样我们就可以 O ( 1 ) O(1) O(1)求出一个数在第 i i i~ j j j块出现的次数

v a l [ i ] [ j ] val[i][j] val[i][j]表示第 i i i块到第 j j j块内出现了偶数次的数的个数

这样对于每次询问,我们直接 O ( 1 ) O(1) O(1)得到所有整块内的答案

然后再对两边长为 n \sqrt{n} n 的区间统计一下每个数出现的次数,再和整块内的数的出现次数分类讨论一下得到答案

复杂度 O ( n n + m n ) O(n\sqrt{n}+m\sqrt{n}) O(nn +mn )

其实由均值法分析的最适合的块的大小是 n l o g n \sqrt{nlog_n} nlogn

但空间开不下就只能 n \sqrt{n} n

注意细节!!

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	int res=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
	return res;
}
const int N=100005;
const int M=318;
int n,c,buc[N],plc[N],m,cnt,blo,l[M],ans,r[M],num[M][N],a[N],val[M][M];
int main(){
	n=read(),c=read(),m=read();
	for(int i=1;i<=n;++i)a[i]=read();
	blo=sqrt(n),cnt=(n-1)/blo+1;
	for(int i=1;i<=n;i++)plc[i]=(i-1)/blo+1;
	for(int i=1;i<=cnt;i++)l[i]=(i-1)*blo+1,r[i]=min(i*blo,n);
	for(int i=1;i<=n;i++){
		for(int j=plc[i];j<=cnt;j++){
			num[j][a[i]]++;
		}
	}
	for(int i=1;i<=cnt;i++){
		int tmp=0;
		for(int j=l[i];j<=n;j++){
			if(buc[a[j]]&1)tmp++;
			else if(buc[a[j]]>0)tmp--;
			buc[a[j]]++;
			if(j==r[plc[j]])val[i][plc[j]]=tmp;
		}
		memset(buc,0,sizeof(buc));
	}
	while(m--){
		int x=(read()+ans)%n+1,y=(read()+ans)%n+1;
		if(x>y) swap(x,y);ans=0;
		if(plc[x]==plc[y]||plc[x]+1==plc[y]){
			for(int i=x;i<=y;i++)buc[a[i]]++;
			for(int i=x;i<=y;i++){
				if(buc[a[i]]==0)continue;
				if(!(buc[a[i]]&1))ans++;
				buc[a[i]]=0;
			}
		}
		else{
			ans=val[plc[x]+1][plc[y]-1];
			for(int i=x;i<=r[plc[x]];i++)buc[a[i]]++;
			for(int i=l[plc[y]];i<=y;i++)buc[a[i]]++;//先全部加进桶里
			for(int i=x;i<=r[plc[x]];i++){
				if(buc[a[i]]==0)continue;
				int tmp=num[plc[y]-1][a[i]]-num[plc[x]][a[i]];
				if(tmp==0){//注意这里if语句的顺序
					if(!(buc[a[i]]&1))ans++;
				}
				else if((tmp&1)&&(buc[a[i]]&1))ans++;
				else if((!(tmp&1))&&(buc[a[i]]&1))ans--;
				buc[a[i]]=0;
			}
			for(int i=l[plc[y]];i<=y;i++){
				if(buc[a[i]]==0)continue;
				int tmp=num[plc[y]-1][a[i]]-num[plc[x]][a[i]];
				if(tmp==0){//注意这里if语句的顺序
					if(!(buc[a[i]]&1))ans++;
				}
				else if((tmp&1)&&(buc[a[i]]&1))ans++;
				else if((!(tmp&1))&&(buc[a[i]]&1))ans--;
				buc[a[i]]=0;
			}
		}
		cout<<ans<<'\n';
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值