洛谷P5048 Yuno loves sqrt technology III
题意
给你一个长为 (n)的序列 (a),(m) 次询问,每次查询一个区间的众数的出现次数,强制在线。
(1leq n,m leq 5 imes 10^5,0leq a_ileq 10^9)
题解
分块,设块长为(S),先预处理第(i)块到第(j)块的众数的出现次数(f[i][j]),对于值域于中的每一个值,用vector按顺序记录(a)中出现该值的下标,同时记录(a)中每个元素在其对应vector中的下标。对于每一次询问(l,r),假设其包含的连续完整块的答案为(ans),由于非完整块中的元素最多(2S)个,故最多使答案增加(2S),我们依次检查这些元素能否是答案增大。对于左边的非完整块的元素(a_i),若其对应vector为(g),其在(g)中的下标为(p),如果(g[p+ans])存在且(g[p+ans]leq r),说明(a_i)在([l,r])中的出现次数不会比(ans)更差,故可直接++(ans);对于右边的元素同理。注意每个检查只是判定了能否使(ans)变为(ans+1),而某个位置上的值可能使答案不止增大1,故需对每个位置需用while循环来进行检查。
预处理部分时间复杂度(O((frac{n}{S})^2 imes S)=O(frac{n^2}{S})),询问部分复杂度(O(mS)),故总复杂度(O(frac{n^2}{S}+mS)),取(S=frac{n}{sqrt m})可得总复杂度(O(nsqrt m)),直接取(S=sqrt n),复杂度(O((n+m)sqrt n))也可通过。
#include <bits/stdc++.h>
#define pb(x) emplace_back(x)
using namespace std;
const int N=600100,M=1000;
int id[N],f[M][M],n,m,b[N],c[N],nm,d[N],a[N],bs;
vector<int> g[N];
int ck1(int ans,int x){
int k=a[x],na=g[k].size(),p=d[x];
if(p+ans<na)return g[k][p+ans];
else return n+1;
}
int ck2(int ans,int x){
int k=a[x],p=d[x];
if(p-ans>=0)return g[k][p-ans];
else return -1;
}
void f1(){
scanf("%d%d",&n,&m);
bs=sqrt(n)+1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
id[i]=(i-1)/bs+1;
}
nm=(n-1)/bs+1;
sort(b+1,b+1+n);
int np=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+1+np,a[i])-b;
g[a[i]].pb(i);d[i]=g[a[i]].size()-1;
assert(g[a[i]][d[i]]==i);
}
for(int i=1;i<=nm;i++){
int na=0;
for(int j=1;j<=np;j++){c[j]=0;}
for(int j=i;j<=nm;j++){
int l=(j-1)*bs+1,r=min(l+bs-1,n);
for(int k=l;k<=r;k++){
c[a[k]]++;
na=max(na,c[a[k]]);
}
f[i][j]=na;
}
}
int lstans=0;
while(m--){
int x,y;scanf("%d%d",&x,&y);
x^=lstans;y^=lstans;
int na=id[x],nb=id[y];
int ans=0;
if(na+1<=nb-1){ans=f[na+1][nb-1];}
for(int i=min(y,na*bs);i>=x;i--){
while(ck1(ans,i)<=y){
++ans;
}
}
for(int i=max(x,(nb-1)*bs+1);i<=y;i++){
while(ck2(ans,i)>=x){
++ans;
}
}
lstans=ans;
printf("%d
",ans);
}
}
int main(){
f1();
return 0;
}
/*
10 0
2 3 5 1 5 5 4 4 3 5
4 1
2 3 3 3
2 4
*/