[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;
}
posted @ 2018-01-02 11:02  Mrha  阅读(171)  评论(0编辑  收藏  举报