P4168 [Violet] 蒲公英 题解(区间众数)

P4168 [Violet] 蒲公英

求区间众数,强制在线。
考虑分块,预处理出来两个数组,\(s_{i,j}\) 表示在前 \(i\) 个块中,数字 \(j\) 出现了多少次,类似前缀和的一个东西。\(p_{i,j}\) 表示第 \(i\) 个块到第 \(j\) 个块的众数信息。
\(s_{i,j}\) 的处理直接前缀和即可, \(p_{i,j}\) 处理时借助 \(s\) 数组即可。
处理完之后我们考虑区间 \([i,j]\) 的众数如何产生。
image

\(l\)\(r\) 之间的块的众数信息我们已经提前处理出来了(蓝色部分),对于绿色部分我们直接暴力扫描,拿一个桶来记录每个数的出现次数,然后加上他们在 \(l\)\(r\) 之间的块的出现次数,来更更新当前的答案。
然后就做完了,每次询问后记得清空桶数组。时间复杂度和空间复杂度都为 \(O(n\sqrt n)\)

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    return x*f;
}
const int N=4e4+10,LEN=300;
int a[N],n,m,len,temp[N],id[N],s[LEN][N],t[N],lastans;
struct BLOCK{int l,r;}block[LEN];
struct TEP{int x,num;}p[LEN][LEN];
inline int ask(int l,int r){
    l=(l+lastans-1)%n+1,r=(r+lastans-1)%n+1;
    if(l>r)std::swap(l,r);
    int sid=id[l],eid=id[r];
    TEP max={0,0};
    if(eid-sid<=1){
        for(int i=l;i<=r;++i)t[a[i]]++;
        for(int i=l;i<=r;++i){
            if(t[a[i]]>=max.num){
                if(t[a[i]]==max.num&&a[i]>max.x)continue;;
                max={a[i],t[a[i]]};
            }
        }
        for(int i=l;i<=r;++i)t[a[i]]=0;
        return lastans=temp[max.x];
    }
    max=p[sid+1][eid-1];
    for(int i=l;id[i]==sid;++i)t[a[i]]++;
    for(int i=r;id[i]==eid;--i)t[a[i]]++;
    max.num+=t[max.x];
    for(int i=l;id[i]==sid;++i){
        int zc=t[a[i]]+s[eid-1][a[i]]-s[sid][a[i]];
        if(zc>max.num||(zc==max.num&&a[i]<max.x))max={a[i],zc};
    }
    for(int i=r;id[i]==eid;--i){
        int zc=t[a[i]]+s[eid-1][a[i]]-s[sid][a[i]];
        if(zc>max.num||(zc==max.num&&a[i]<max.x))max={a[i],zc};
    }
    for(int i=l;id[i]==sid;++i)t[a[i]]=0;
    for(int i=r;id[i]==eid;--i)t[a[i]]=0;
    return lastans=temp[max.x];
}
signed main(){
    // freopen("in.in","r",stdin),freopen("out.out","w",stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    n=read(),m=read();len=std::sqrt(n);
    for(int i=1;i<=n;++i){
        temp[i]=a[i]=read();
        id[i]=(i-1)/len+1;
        if(id[i]!=id[i-1])block[id[i]].l=i;
        block[id[i]].r=i;
    }
    std::stable_sort(temp+1,temp+1+n);
    int zc=std::unique(temp+1,temp+n+1)-temp-1;
    for(int i=1;i<=n;++i)a[i]=std::lower_bound(temp+1,temp+zc+1,a[i])-temp;
    for(int k=1;k<=id[n];++k){
        for(int i=1;i<=zc;++i)s[k][i]=s[k-1][i];
        for(int i=block[k].l;i<=block[k].r;++i)s[k][a[i]]++;
    }
    for(int i=1;i<=id[n];++i)
        for(int j=i;j<=id[n];++j){
            TEP max=p[i][j-1];
            for(int k=block[j].l;k<=block[j].r;++k){
                if(s[j][a[k]]-s[i-1][a[k]]>=max.num){
                    if(s[j][a[k]]-s[i-1][a[k]]==max.num&&a[k]>max.x)continue;
                    max={a[k],s[j][a[k]]-s[i-1][a[k]]};
                }
            }
            p[i][j]=max;
        }
    for(int i=1,l,r;i<=m;++i)
        l=read(),r=read(),std::cout<<ask(l,r)<<'\n';
}

然后这个题的卡常版为 P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III,需要将空间复杂度优化到 \(O(n)\),之后写。
对于蒲公英过这道题,lxl如是说
image

posted @ 2024-02-02 16:22  Ishar-zdl  阅读(274)  评论(3)    收藏  举报