分块9:求区间众数(若存在多个众数,输出值最小的)

 

警戒:以后类似于没看到强制在线这种傻逼错误千万不要犯啊。

 

 

问题描述:

在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为n的序列 (a_1,a_2..a_n)(a1​,a2​..an​) ,其中 a_iai​ 为一个正整数,表示第i棵蒲公英的种类编号。

而每次询问一个区间 [l,r],你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

注意,你的算法必须是在线的

输入输出格式

输入格式:

 

第一行两个整数 n,m ,表示有n株蒲公英,m 次询问。

接下来一行n个空格分隔的整数 a_iai​ ,表示蒲公英的种类

再接下来m 行每行两个整数 l_0,r_0l0​,r0​ ,我们令上次询问的结果为 x(如果这是第一次询问, 则 x=0)。

令 l=(l_0+x-1)\bmod n + 1,r=(r_0+x-1) \bmod n + 1l=(l0​+x−1)modn+1,r=(r0​+x−1)modn+1 ,如果 l>r,则交换 l,r 。

最终的询问区间为[l,r]。

 

输出格式:

 

输出m 行。每行一个整数,表示每次询问的结果。

 

 

分析:

   从之前用分块代替树状数组求区间和的例子我们可以知道,分块是把一个区间分解成若干个小的区间,先通过预处理求出这些小区间的一些值,通过空间换时间达成时空平衡。

   代替树状数组的思路很简单,我们把n个数分成T个区间,然后对每个区间求出区间和,并且开一个数组add,存储整个区间需要加的值。如果我们需要对整个区间进行修改,那么把区间所包含的完整的块儿,直接在add数组里加上一个值,对不完整的块儿,我们直接暴利枚举,在a数组的基础上加上所要加的值。假如说,我们的块儿的长度为根号n,那么一次区间加上一个数的操作,复杂度就是O(sqrt(n)),当然查询的时候也是O(sqrt(n))了。

    但是这道题我们求得是区间众数,区间众数是不可以相加减的。我们考虑,如果我们已经知道了一个块儿的众数(这个块儿的大小先不说),并且知道了这个块儿所代表的区间内的所有数出现的次数,那么当我们查询l,r之间的众数时,这个众数无非两种情况,一个块儿内的数,一个是块儿外的小范围的数。我们做的就是,每一个块儿带一个数组存储出现的数的次数,然后暴利枚举小范围,加到块儿的数组里面,然后求出众数,之后再回复原状。为了维护我们的信息,假如说我们把n分成t个块儿,我们不仅维护这t个块儿,还要维护这些块儿组成的大块儿。

   那么我们预处理的复杂度是n*t*t,每次询问的复杂度是n/t,所有的询问就是m*n/t。我们让这两个复杂度相等,发现此时t≈n的立方根。整个算法是可以A掉题的。

   然后最傻逼的就是,我这道题上午就写出了板子,一道板子题,花了一天的时间。因为我没看到题目输出要求,这个是强制在线题,也就是答案与上一个有关系!

   哎,心累。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
int p,q,t,n,m,l,r,L,lan,pos,most[1600],maxn[1600],sum[1600][41000],a[41000],b[40],temp[41000],c[41000];
void discrete()
{
	sort(c+1,c+n+1);
	int sz=unique(c+1,c+n+1)-c-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+sz+1,a[i])-c;
}
int ask(int l,int r)
{
	l=(l+lan-1)%n+1;
	r=(r+lan-1)%n+1;
	if(l>r) swap(l,r);
	p=l/L;q=r/L;
	if(l%L==1)p++;
	else if(l%L) p+=2;
	else p++;
	int ans;
	if(q-p<0){
		int maxx=0;
		for(int i=l;i<=r;i++){ 
			temp[a[i]]++;
			if(temp[a[i]]>maxx)
				maxx=temp[a[i]],ans=a[i];
			if(temp[a[i]]==maxx&&c[a[i]]<c[ans])
				ans=a[i];
		}
		for(int i=l;i<=r;i++) temp[a[i]]--;
		return c[ans];
	}else{
		pos=b[p-1]+q-p+1;int maxx=maxn[pos];ans=most[pos];
		for(int i=l;i<=L*(p-1);i++){
			sum[pos][a[i]]++;
			if(sum[pos][a[i]]>maxx)
				maxx=sum[pos][a[i]],ans=a[i];
			if(sum[pos][a[i]]==maxx&&c[a[i]]<c[ans])
				ans=a[i];
		}
		for(int i=L*q+1;i<=r;i++){
			sum[pos][a[i]]++;
			if(sum[pos][a[i]]>maxx)
				maxx=sum[pos][a[i]],ans=a[i];
			if(sum[pos][a[i]]==maxx&&c[a[i]]<c[ans])
				ans=a[i];
		}
		for(int i=l;i<=L*(p-1);i++) sum[pos][a[i]]--;
		for(int i=L*q+1;i<=r;i++) sum[pos][a[i]]--;
		return c[ans] ;
	}
}
int main()
{
	//freopen("kgx.in","r",stdin);
	//freopen("kgx.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[i]=a[i];
	discrete();
	t=pow(1.0*n,1.0/3.0);L=n/t;
	for(int i=1;i<=t;i++) b[i]=b[i-1],b[i]+=t-i+1;
	for(int i=1;i<=t;i++)
		for(int j=1;j<=t-i+1;j++){
			pos=b[i-1]+j;
			int maxx=0;int ans;
			for(int k=L*(i-1)+1;k<=L*(i+j-1);k++){
				sum[pos][a[k]]++;
				if(sum[pos][a[k]]>maxx)
					maxx=sum[pos][a[k]],ans=a[k];
				if(sum[pos][a[k]]==maxx&&c[a[k]]<c[ans])
				ans=a[k];
			}
			most[pos]=ans;maxn[pos]=maxx;
		}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&l,&r);
		printf("%d\n",lan=ask(l,r));
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值