[bzoj] 2724 蒲公英 || 分块
原题
强制在线,求[l,r]内的众数
是分块的模板题。
分块:
把序列分为\(\sqrt(n)\)块,预处理好每个块内的答案,询问时整块直接求解,非整块暴力求解。因为非整块的数不会超过\(2\sqrt(n)\)个,所以算法的时间复杂度为\(O((n+m)\sqrt(n))\)
本题分块后,预处理两个数组:
f[i][j]:第i块到第j块的答案(从i的左端点到j的右端点)
cnt[i][j]:从序列开始到第j块结束,i数出现了多少次
每次将非整块的答案处理出来后,与cnt结合比较出答案即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 40010
#define B 210
#define inf 0x3f3f3f3f
using namespace std;
int n,m,num,s,l,r,ans,a[N],b[N],sum[N],idx;
int bl[B],br[B],f[B][B],cnt[N][B];
bool v[N];
int read()
{
int ans=0,fu=1;
char j=getchar();
for (;j<'0' || j>'9';j=getchar()) if (j=='-') fu=-1;
for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
return ans*fu;
}
void init()//预处理cnt和f
{
int k,cur,mxv,t,h,c;
for (int i=1;i<=s;i++)
{
k=bl[i];cur=inf;mxv=-inf;
for (int j=k;j<n;j++)
sum[a[j]]=0;
for (int j=i;j<=s;j++)
{
t=br[j];
while (k<=t)
{
c=++sum[a[k]];
if (c>mxv) mxv=c,cur=a[k];
else if (c==mxv && a[k]<cur) cur=a[k];
++k;
}
f[i][j]=cur;
}
}
memset(sum,0,sizeof(sum));
for (int i=1;i<=s;i++)
{
for (int j=0;j<idx;j++) cnt[j][i]=cnt[j][i-1];
h=bl[i];
t=br[i];
while (h<=t) cnt[a[h]][i]=++sum[a[h]],++h;
}
}
int query(int l,int r)
{
int mxv=-inf,ret=inf,c;
if (r-l<2*s)//如果长度<2*s,直接暴力即可
{
for (int i=l;i<=r;i++)
if (!v[a[i]]) v[a[i]]=1,sum[a[i]]=1;
else ++sum[a[i]];
for (int i=l;i<=r;i++)
if (v[a[i]])
{
if (sum[a[i]]>mxv) mxv=sum[a[i]],ret=a[i];
else if (sum[a[i]]==mxv && a[i]<ret) ret=a[i];
v[a[i]]=0;
}
return b[ret];
}
int L=l/num+1,R=r/num+1,st,en;
if (l==bl[L]) --L;
if (r==br[R]) ++R;
ret=f[L+1][R-1];//整块部分的众数
mxv=cnt[ret][R-1]-cnt[ret][L];//处理出整块中众数的次数
en=br[L];st=bl[R];
for (int i=l;i<=en;i++)//暴力
if (!v[a[i]]) v[a[i]]=1,sum[a[i]]=1;
else ++sum[a[i]];
for (int i=st;i<=r;i++)
if (!v[a[i]]) v[a[i]]=1,sum[a[i]]=1;
else ++sum[a[i]];
for (int i=l;i<=en;i++)
if (v[a[i]])
{
c=cnt[a[i]][R-1]-cnt[a[i]][L];
c+=sum[a[i]];
if (c>mxv) mxv=c,ret=a[i];
else if (c==mxv && a[i]<ret) ret=a[i];
v[a[i]]=0;
}
for (int i=st;i<=r;i++)
if (v[a[i]])
{
c=cnt[a[i]][R-1]-cnt[a[i]][L];
c+=sum[a[i]];
if (c>mxv) mxv=c,ret=a[i];
else if (c==mxv && a[i]<ret) ret=a[i];
v[a[i]]=0;
}
return b[ret];
}
int main()
{
n=read();
m=read();
for (int i=0;i<n;i++) a[i]=b[i]=read();
sort(b,b+n);
idx=unique(b,b+n)-b;
for (int i=0;i<n;i++)
a[i]=lower_bound(b,b+idx,a[i])-b;//离散化
num=sqrt(n);
for (int i=0;i<n;i++)
if (i%num==0) br[s]=i-1,bl[++s]=i;//bl[i]表示第i块的左端点,br[i]表示第i块的右端点
br[s]=n-1;
bl[s+1]=br[s+1]=n;
init();
while (m--)
{
l=read();r=read();
l=(l+ans-1)%n;
r=(r+ans-1)%n;
if (l>r) swap(l,r);
printf("%d\n",ans=query(l,r));
}
return 0;
}