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

\(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如是说


浙公网安备 33010602011771号