题目传送门
莫队
将出现次数进行分块,再将每种出现次数的值进行分块
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=40000+100;
int ans[maxn];
int num[maxn];
int cnt[maxn];
int n,m;
int R[maxn];
int Rc[210],Rcnum[maxn][210];
int size,block;
struct Mo{
int l,r;
int c,k;
int id;
}mo[maxn];
int Ans[maxn];
inline bool cmp(Mo a,Mo b){
return R[a.l]==R[b.l]?a.r<b.r:R[a.l]<R[b.l];
}
inline void add(int ind){
int tmp=ans[ind];
int c= ++num[tmp];
cnt[c]++;
Rcnum[c][tmp/size]++;
if(cnt[c]==1) Rc[c/size]++;
c--;
cnt[c]--;
Rcnum[c][tmp/size]--;
if(c!=0 && cnt[c]==0) Rc[c/size]--;
}
inline void del(int ind){
int tmp=ans[ind];
int c= --num[tmp];
cnt[c]++;
Rcnum[c][tmp/size]++;
if(c!=0 && cnt[c]==1) Rc[c/size]++;
c++;
cnt[c]--;
Rcnum[c][tmp/size]--;
if(cnt[c]==0) Rc[c/size]--;
}
int main(){
scanf("%d",&n);
size=block=200;
for(int i=1;i<=n;i++) scanf("%d",&ans[i]),R[i]=i/size;
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d%d%d%d",&mo[i].l,&mo[i].r,&mo[i].c,&mo[i].k),mo[i].id=i;
sort(mo+1,mo+1+m,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l>mo[i].l) add(l-1),l--;
while(r<mo[i].r) add(r+1),r++;
while(l<mo[i].l) del(l),l++;
while(r>mo[i].r) del(r),r--;
int c,j,t,g,k;
c=k=0;
for(j=0;j<=block;j++){
if(c+Rc[j]>=mo[i].c) break;
else c+=Rc[j];
}
for(t=size*j;t<size*(j+1);t++){
if(t!=0 && cnt[t]) c++;
if(c==mo[i].c) break;
}
for(j=0;j<=block;j++){
if(k+Rcnum[t][j]>=mo[i].k) break;
else k+=Rcnum[t][j];
}
for(g=size*j;g<size*(j+1);g++){
if(num[g]==t) k++;
if(k==mo[i].k) break;
}
Ans[mo[i].id]=g;
}
for(int i=1;i<=m;i++) printf("%d\n",Ans[i]);
}