牛客练习赛10 E:数列查找

题目传送门
莫队
将出现次数进行分块,再将每种出现次数的值进行分块

代码:

#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]);
}

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/81359306