警戒:以后类似于没看到强制在线这种傻逼错误千万不要犯啊。
问题描述:
在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。
为了简化起见,我们把所有的蒲公英看成一个长度为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;
}