[FROM LUOGU][Violet]蒲公英

传送门

SOL
暴力枚举肯定会T
因此我们需要思考如何快速维护区间众数
线段树之类的数据结构复杂度较好,但是在这种情况下不能够区间合并(区间众数合并之后不一定还是众数),而且这道题也没有修改操作
再看看这个不大不小的数据规模,应该是 O ( N ∗ N ) O(N*\sqrt N) O(NN )的算法,于是考虑分块
分块虽然也不能支持区间合并,但是我们可以利用分块特性预处理出每个块的答案
用一个 i n t int int数组 s s s维护每种蒲公英的出现次数
用一个 p a i r pair pair数组 p p p维护第 i i i块到第 j j j块的区间众数及其出现次数
对于一个询问,我们只用考虑中间完整块的答案,并暴力统计一下两边不完整区间的答案进行比较即可
细节:
注意离散化编号之后要存储反向映射,因为输出要使用原编号
每次暴力统计后数组清空,但不能全部清空,只清空接下来会用到的,否则复杂度会假

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
inline int rd(){
	int re data=0;static char ch=0;ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data;
}
const int N=40005,K=205;
#define mp make_pair
#define cnt first
#define val second
typedef pair<int,int> pii;
int n,m,s[K][N],sec,rev[N],last,tot,rank,num[N];
bool vis[N];
pii p[K][K];
struct node{int v,x,id;}a[N];
inline bool cmp1(node a,node b){if(a.x==b.x)return a.id<b.id;return a.x<b.x;}
inline bool cmp2(node a,node b){return a.id<b.id;}
signed main(){
	n=rd(),m=rd();
	for(int re i=1;i<=n;++i)a[i].x=rd(),a[i].id=i;
	sort(a+1,a+n+1,cmp1);
	for(int re i=1;i<=n;++i){
		if(a[i].x!=a[i-1].x)rev[++rank]=a[i].x;
		a[i].v=rank;
	}
	sort(a+1,a+n+1,cmp2);
	sec=sqrt(n),tot=(n+sec-1)/sec;
	for(int re maxl,i=1;i<=tot;++i){
		for(int re j=1;j<=n;++j)s[i][a[j].v]=s[i-1][a[j].v];
		maxl=min(n,i*sec);
		for(int re j=(i-1)*sec+1;j<=maxl;++j)++s[i][a[j].v];
	}
	for(int re i=1;i<=tot;++i){
		memset(num,0,sizeof num);
		pii re tmp=mp(0,0);
		for(int re maxl,j=i;j<=tot;++j){
			maxl=min(n,j*sec);
			for(int re k=(j-1)*sec+1;k<=maxl;++k){
				++num[a[k].v];
				if(num[a[k].v]>tmp.cnt)tmp=mp(num[a[k].v],a[k].v);
				else if(num[a[k].v]==tmp.cnt)tmp.val=min(tmp.val,a[k].v);
			}p[i][j]=tmp;
		}
	}memset(num,0,sizeof num);
	while(m--){
		int re l=rd(),r=rd(),lsec,rsec;
		l=(l+last-1)%n+1,r=(r+last-1)%n+1;
		if(l>r)swap(l,r);
		lsec=(l+sec-1)/sec,rsec=(r+sec-1)/sec;
		if(rsec-lsec<=2){
			for(int re i=l;i<=r;++i)num[a[i].v]=0;
			int re ans=0;
			for(int re i=l;i<=r;++i){
				++num[a[i].v];
				if(num[a[i].v]>num[ans])ans=a[i].v;
				else if(num[a[i].v]==num[ans])ans=min(ans,a[i].v);
			}printf("%d\n",last=rev[ans]);
		}else{
			int re maxl=min(n,lsec*sec),ans=p[lsec+1][rsec-1].val;
			num[ans]=vis[ans]=0;
			for(int re i=l;i<=maxl;++i)num[a[i].v]=vis[a[i].v]=0;
			for(int re i=(rsec-1)*sec+1;i<=r;++i)num[a[i].v]=vis[a[i].v]=0;
			for(int re i=l;i<=maxl;++i)++num[a[i].v];
			for(int re i=(rsec-1)*sec+1;i<=r;++i)++num[a[i].v];
			pii re tmp=mp(0,0);
			for(int re now,i=l;i<=maxl;++i){
				if(vis[a[i].v])continue;vis[a[i].v]=1;
				now=num[a[i].v]+s[rsec-1][a[i].v]-s[lsec][a[i].v];
				if(now>tmp.cnt)tmp=mp(now,a[i].v);
				else if(now==tmp.cnt)tmp.val=min(tmp.val,a[i].v);
			}
			for(int re now,i=(rsec-1)*sec+1;i<=r;++i){
				if(vis[a[i].v])continue;vis[a[i].v]=1;
				now=num[a[i].v]+s[rsec-1][a[i].v]-s[lsec][a[i].v];
				if(now>tmp.cnt)tmp=mp(now,a[i].v);
				else if(now==tmp.cnt)tmp.val=min(tmp.val,a[i].v);	
			}
			if(tmp.cnt>num[ans]+p[lsec+1][rsec-1].cnt)ans=tmp.val;
			else if(tmp.cnt==num[ans]+p[lsec+1][rsec-1].cnt)ans=min(tmp.val,ans);
			printf("%d\n",last=rev[ans]);
		}
	}exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值