蒲公英(bzoj2724)(分块+区间众数)

Input

Output

Sample Input

6 3 
1 2 3 2 1 2 
1 5 
3 6 
1 5

Sample Output

1 
2 
1 

HINT

\(n <= 40000\),$ m <= 50000$

题意:

求区间众数

题解:

见代码

//解决本题的重要性质: 
//对于两个区间a,b,其中已知a区间的众数k 
//则众数一定为k或是b区间的任意一个数  
#include<bits/stdc++.h>
#define re register int 
using namespace std;
const int N=40010,M=410;
int n,q,m,blen,bsum;
int a[N],b[N];//b为离散数组 
int bl[M][M];//bl[i][j]表示第i个块中的第j个数,bl[i][0]表示第i个块的长度 
int bk[N];//bk[i]表示第i个数(在原数列中)在第bk[i]个块中 
int f[M][M];//f[i][j]表示第i块到第j块之间的众数 
int g[N][M];//g[i][j]表示i在前j个块中出现的次数 
void init(){//初始化 
	for(int i=1,j=1;i<=n;++j){
		int k;
		for(k=1;k<=blen&&i<=n;++i,++k){
			bk[i]=j;
			bl[j][k]=a[i];
		}k--;
		bl[j][0]=k;
		bsum=j;
	}//处理块 
	for(int i=1;i<=bsum;++i){
		for(int j=1;j<=m;++j){
			g[j][i]=g[j][i-1];
		}
		for(int j=1;j<=bl[i][0];++j){
			g[bl[i][j]][i]++;
		}
	}//预处理g数组 
	for(int i=1;i<=bsum;++i){
		for(int j=i;j<=bsum;++j){
			int num=f[i][j-1];int mx=g[num][j]-g[num][i-1];
			for(int k=1;k<=bl[j][0];++k){
				int now=g[bl[j][k]][j]-g[bl[j][k]][i-1];
				if(now>mx||(now==mx&&bl[j][k]<num))num=bl[j][k],mx=now;
			}
			f[i][j]=f[j][i]=num;
		}
	}//预处理f数组 
}
void read(){//读入 
	cin>>n>>q;blen=sqrt(n);
	for(int i=1;i<=n;++i)scanf("%d",a+i),b[i]=a[i];
}
void lsh(){
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;++i)a[i]=lower_bound(b+1,b+m+1,a[i])-b;
}
void work(){
	int last=0;
	while(q--){
		int l,r;
		scanf("%d%d",&l,&r);
		l=(l+last-1)%n+1;
		r=(r+last-1)%n+1;
		if(l>r)swap(l,r);
		static int bj[M],cnt,v[N];cnt=0;//bj记录边角的数据,cnt为边角数据的数量
		int L,R,num,mx; 
		if(bk[l]==bk[r]){//在同一块内暴力求众数 
			for(int i=l;i<=r;++i)bj[++cnt]=a[i],v[a[i]]++;
			mx=0;
			for(int i=l;i<=r;++i){
				if(v[a[i]]>mx||(v[a[i]]==mx&&a[i]<num))num=a[i],mx=v[a[i]];
			}
			printf("%d\n",last=b[num]);
		}else{//在不同块时,将中间当成一大块和边角比较
		      //根据性质,众数只有可能是中间这一块的众数或是边角上的数
			  //所以暴力枚举再判断就行了 
			re i;
			for(i=l;bk[i]==bk[i-1];++i){
				bj[++cnt]=a[i];v[a[i]]++;
			}L=bk[i]; 
			for(i=r;bk[i]==bk[i+1];--i){
				bj[++cnt]=a[i];v[a[i]]++;
			}R=bk[i];
		    num=f[L][R],mx=v[num]+g[num][R]-g[num][L-1];
		    for(i=1;i<=cnt;++i){
		    	int now=v[bj[i]]+g[bj[i]][R]-g[bj[i]][L-1];
		    	if(now>mx||(now==mx&&bj[i]<num))num=bj[i],mx=now;
			}
			printf("%d\n",last=b[num]);
		}
		for(re i=1;i<=cnt;++i)--v[bj[i]];//v数组要这样清空,复杂度O(cnt),不能用memset,那样是O(n)
	}
}
int main(){
	read();
	lsh();
	init();
	work(); 
}
posted @ 2018-12-11 09:56  整理者  阅读(385)  评论(0编辑  收藏  举报