【BZOJ2724】蒲公英 题解(分块+区间众数)

题目链接

题目大意:给定一段长度为$n$的序列和$m$次询问,每次询问区间$[l,r]$内的最小的众数。$n\leq 40000,a_i\leq 10^9$

-----------------------------

因为$a_i\leq 10^9$,显然不能开那么大的数组。所以要离散化。对于离散化后的数组,我们维护两个值$sum[i][j]$和$p[i][j]$。$sum[i][j]$表示前$i$个块中$j$出现的次数,这个$O(n \sqrt n)$暴力枚举就好。$p[i][j]$表示块$i$到$j$的众数,这个只需开一个桶维护,然后$O(\sqrt n \sqrt n \sqrt n)$暴力枚举就好。

对于查询,我们仍然暴力把区间$[l,r]$中边边角角的部分暴力求出来,对于整块我们用之前维护的$sum$和$p$即可。

其实就是一个大模拟……细节有点小多。不会真的有人连敲代码都不会吧

代码(有些细节的地方有注释):

using namespace std;
const int maxn=40005;
int n,m,block,tot,sum[205][maxn];
int last,pre[maxn],tmpnum[maxn],bucket[maxn],vis[maxn];
struct Node
{
    int id,val,se;//id编号,val值,se离散化后的值 
}a[40005];
struct node
{
    int num,s;
}p[205][205];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
bool cmp1(Node a,Node b){return a.val<b.val;}//离散化排序
bool cmp2(Node a,Node b){return a.id<b.id;}
int getpos(int x)//不用维护每个区间的左右端点了,一个函数搞定
{
    int pos=x/block;
    if (x%block) pos++;
    return pos; 
}
inline void build()
{
    for (int i=1;i<=tot;i++)
    {
        memset(bucket,0,sizeof(bucket));node tmp;//注意清空桶
        tmp.s=tmp.num=0;
        for (int j=i;j<=tot;j++)
        {
            for (int k=(j-1)*block+1;k<=min(n,j*block);k++)//预处理
            {
                bucket[a[k].se]++;
                if (bucket[a[k].se]>tmp.s)
                {
                    tmp.s=bucket[a[k].se];
                    tmp.num=a[k].se;
                }
                else if (bucket[a[k].se]==tmp.s) tmp.num=min(tmp.num,a[k].se);
            }
            p[i][j]=tmp;
        }
    }
    for (int i=1;i<=tot;i++){//预处理
        for (int j=1;j<=n;j++) sum[i][a[j].se]=sum[i-1][a[j].se];
        for (int j=(i-1)*block+1;j<=min(n,i*block);j++) sum[i][a[j].se]++;
    }
}
inline void query(int l,int r)
{
    int posl=getpos(l),posr=getpos(r);
    if (posr-posl<=2)//如果区间范围较小直接暴力枚举即可
    {
        int ans=0;
        for (int i=l;i<=r;i++) tmpnum[a[i].se]=0;//开一个桶,注意清空
        for (int i=l;i<=r;i++){
            tmpnum[a[i].se]++;
            if (tmpnum[a[i].se]>tmpnum[ans]) ans=a[i].se;
            else if (tmpnum[a[i].se]==tmpnum[ans]) ans=min(ans,a[i].se);
        }
        printf("%d\n",last=pre[ans]);
        return;
    }
    int ans=p[posl+1][posr-1].num,maxsum=0,maxnum;//用预处理的p数组维护ans
    vis[ans]=0;tmpnum[ans]=0;
////////////暴力把边边角角统计出来//////////////
    for (int i=l;i<=min(n,posl*block);i++) tmpnum[a[i].se]=0,vis[a[i].se]=0;
    for (int i=(posr-1)*block+1;i<=r;i++) tmpnum[a[i].se]=0,vis[a[i].se]=0;
    for (int i=l;i<=min(n,posl*block);i++) tmpnum[a[i].se]++;
    for (int i=(posr-1)*block+1;i<=r;i++) tmpnum[a[i].se]++;
    for (int i=l;i<=min(n,posl*block);i++){
        if (vis[a[i].se]) continue;
        vis[a[i].se]=1;
        int summ=tmpnum[a[i].se]+sum[posr-1][a[i].se]-sum[posl][a[i].se];//个数 
        if (summ>maxsum) maxsum=summ,maxnum=a[i].se;
        else if (maxsum==summ) maxnum=min(maxnum,a[i].se);
    }
    for (int i=(posr-1)*block+1;i<=r;i++){
        if (vis[a[i].se]) continue;
        vis[a[i].se]=1;
        int summ=tmpnum[a[i].se]+sum[posr-1][a[i].se]-sum[posl][a[i].se];//个数 
        if (summ>maxsum) maxsum=summ,maxnum=a[i].se;
        else if (maxsum==summ) maxnum=min(maxnum,a[i].se);
    }
/////////////////////////////////////////////// 
    if (maxsum>tmpnum[ans]+p[posl+1][posr-1].s) ans=maxnum;
    else if (maxsum==tmpnum[ans]+p[posl+1][posr-1].s) ans=min(ans,maxnum);
    printf("%d\n",last=pre[ans]);
}
int main()
{
    n=read(),m=read();block=sqrt(n);
    tot=(n+block-1)/block;
    for (int i=1;i<=n;i++) a[i].val=read(),a[i].id=i;
    sort(a+1,a+n+1,cmp1);a[0].val=-1;
    for (int i=1;i<=n;i++)//离散化 
    {
        a[i].se=a[i-1].se;
        if (a[i].val!=a[i-1].val) a[i].se++;
        pre[a[i].se]=a[i].val;
    }
    sort(a+1,a+n+1,cmp2);
    build();
    for (int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        l=(l+last-1)%n+1;
        r=(r+last-1)%n+1;
        if (l>r) swap(l,r);
        query(l,r);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Invictus-Ocean/p/13199601.html