【分块】[LOUGU 作诗] 正偶次分块

题目:

题目链接:[LOUGU 作诗]
题解:
题目就是让求区间的正偶次个数,这里可以仿照区间众数的做法,整体下来进行分块就比较复杂,还需要考虑衔接块和大整块,但是还是比较好思考的。
(重点看注释)

代码:

// luogu-judger-enable-o2
//有是不挺的TLE,,又是要卡常,,,开O2过的
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const int sea=1e5+7;
int n,m,mx,ans,top,num,block,belong[sea],cnt[400][sea],f[400][400],l[sea],a[sea],c[sea],st[sea];
int main()
{
	n=read(); mx=read(); m=read(); ans=0; block=sqrt(n); 
	for(int i=1;i<=n;i++) 
	{
		a[i]=read(),belong[i]=(i-1)/block+1;
        if(belong[i]!=belong[i-1]) l[belong[i]]=i;
	}
	belong[n+1]=belong[n]+1; l[belong[n+1]]=n+1;//建块,跟之前的建块有点不同,这里主要是要每个块的左端点
	for(int i=1;i<=belong[n];i++)
	{
		int t=0;
		for(int j=l[i];j<=n;j++)
		{
			cnt[i][a[j]]++; 
			if((cnt[i][a[j]]>1)&&(cnt[i][a[j]]&1)) t--;
			else if(!(cnt[i][a[j]]&1)) t++;
			if(belong[j]!=belong[j+1]) f[i][belong[j]]=t; 
			//这里用t来处理答案,本人觉得写得比较巧妙。
		}
	}
	//记录每个块从l开始到n时的众数出现的正偶次存在cnt[][]中 ,f[][]是记录每一块的初始答案 
	while(m--)
	{
		int x=read(), y=read();
		x=(x+ans)%n+1,y=(y+ans)%n+1; ans=0;
		if(x>y) swap(x,y);
		//处理读入(在线做) 
		if(belong[x]==belong[y])
		{
			for(int i=x;i<=y;i++) c[a[i]]++, st[++top]=a[i];
			while(top)
			{
				if(c[st[top]])
				ans+=(c[st[top]]&1)^1,c[st[top]]=0;
				top--;
			}
			//栈处理小区间的答案
			printf("%d\n",ans); continue;
		} 
		if(belong[y]-belong[x]>=2) ans=f[belong[x]+1][belong[y]-1];//中间有整块 
		for(int i=x;i<l[belong[x]+1];i++) c[a[i]]++,st[++top]=a[i];
		for(int i=l[belong[y]];i<=y;i++) c[a[i]]++,st[++top]=a[i];
		 //衔接块 
		while(top)
		{
			if(c[st[top]])
			{
				int t=st[top],ins=cnt[belong[x]+1][t]-cnt[belong[y]][t];//这里就是对于衔接点的处理,
				if(ins>0&&(!(ins&1))&&(c[t]&1)) ans--;
				else if(ins>0&&(ins&1)&&(c[t]&1)) ans++;
				else if(!ins&&(!(c[t]&1))) ans++;
				c[t]=0;
			}
			top--;
		}	
		printf("%d\n",ans);	
	}
	return 0;
} 

Continue……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值