[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;
}

转载于:https://www.cnblogs.com/mrha/p/8176190.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值