#分块#洛谷 4168 CH 4401 蒲公英

题目

求区间众数


分析

线段树和树状数组很难做的,分块可以做,离散后预处理,大段维护,小段朴素,就做完了


代码

#include <cstdio>
#include <cmath>
#include <algorithm>
#define cct(i,a,b) for (register int i=a;i<=b;i++)
int n,m,pos[40001],t[40001],a[40001],b[40001],st[201],ed[201],ans[201][201],sum[40001][201];
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
int answ(int l,int r){
	int p=pos[l],q=pos[r];
	if (p==q){//小段朴素
		int now=0;
		cct(i,l,r){
			t[a[i]]++;
			if (t[a[i]]>t[now]||t[a[i]]==t[now]&&now>a[i]) now=a[i];//求众数
		}
		cct(i,l,r) t[a[i]]--;
		return b[now];
	}
	int now=ans[p+1][q-1];
	t[now]+=sum[now][q-1]-sum[now][p];
	cct(i,l,ed[p]){
		if (!t[a[i]]) t[a[i]]+=sum[a[i]][q-1]-sum[a[i]][p];//暂且先记录下来
		t[a[i]]++;
		if (t[a[i]]>t[now]||t[a[i]]==t[now]&&now>a[i]) now=a[i];//求众数
	}
	cct(i,st[q],r){
		if (!t[a[i]]) t[a[i]]+=sum[a[i]][q-1]-sum[a[i]][p];//the same
		t[a[i]]++;
		if (t[a[i]]>t[now]||t[a[i]]==t[now]&&now>a[i]) now=a[i];
	}
	cct(i,l,ed[p]) t[a[i]]=0;//清空
	cct(i,st[q],r) t[a[i]]=0;
	t[ans[p+1][q-1]]=0;
	return b[now];
}
int main(){
	n=in(); m=in(); int pws=sqrt(n); 
	cct(i,1,n){
		a[i]=b[i]=in();
		pos[i]=(i+pws-1)/pws; ed[pos[i]]=i;
		if (!st[pos[i]]) st[pos[i]]=i; //分块
	}
	std::sort(b+1,b+1+n);
	int k=std::unique(b+1,b+1+n)-(b+1);//去重
	cct(i,1,n) a[i]=std::lower_bound(b+1,b+1+k,a[i])-b;//离散
	cct(i,1,pos[n]){
		int now=0;
		cct(j,st[i],n){
			t[a[j]]++;//数字++
			if (t[a[j]]>t[now]||t[a[j]]==t[now]&&now>a[j]) now=a[j];//求众数
			ans[i][pos[j]]=now;//记录答案
		}
		cct(j,st[i],n) t[a[j]]--;
	}
	cct(i,1,n) sum[a[i]][pos[i]]++;
	cct(i,1,k) cct(j,1,pos[n]) sum[i][j]+=sum[i][j-1];//前缀和
	int last=0;
	while (m--){
		int x=in(); int y=in();
		x=(x+last-1)%n+1; y=(y+last-1)%n+1;
		if (x>y) x^=y,y^=x,x^=y;//按照题目模拟
		last=answ(x,y);
		printf("%d\n",last);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值