ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

Description

转眼就要到Karin的生日了!Yuuna她们想为她准备生日礼物!现在有许多礼物被排列成了一个一维序列,每个礼物都有一个价值。Yuuna对这个序列十分感兴趣。因此,你需要多次回答:在某个区间内出现次数第k1少的价值是多少,可能多个不同的价值出现次数均为第k1少,输出其中第k2小的,保证输入合法。注意内存限制
例如:对于一个区间而言(当然不一定是有序的):
1,2,3,4,5,5,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12
权值 出现次数
1 1 出现次数第1少 第1小
2 1 第2小
3 1 第3小
4 1 第4小
5 2 出现次数第2少 第1小
6 2 第2小
7 4 出现次数第3少 第1小
8 4 第2小
9 4 第3小
10 4 第4小
11 6 出现次数第4少 第1小
12 10 出现次数第5少 第1小
若k1=3,k2=2,代表询问这个区间里出现次数第3少的权值中第2小的,则应该输出8。
若k1=5,k2=1,代表询问这个区间里出现次数第5少的权值中第1小的,则应该输出12。
若k1=1,k2=3,代表询问这个区间里出现次数第1少的权值中第3小的,则应该输出3。

Input

第一行包括一个整数n,代表序列的长度。
第二行包括n个整数a1...an,代表该序列。
第三行包括一个整数m,代表询问的次数。
接下来m行,每行包括4个整数l,r,k1,k2,询问al...ar中出现次数第k1少的权值中第k2小的。

Output

对于每个询问,仅输出一行,包括一个整数,代表你的回答。

很显然可以用莫队算法,关键在于维护状态转移。权值分块可以做到O(1)插入/删除,O(√(n))查询第k大,可以用于维护每种出现次数有几个权值以及相同出现次数的不同权值。由均摊分析可知,每种出现次数的权值数之和为n,于是可以预处理离散化并给每个出现次数的权值分块分配对应的空间。可以做到O(n)预处理,O(m√(n))查询,O(n)空间,但常数较大。

#include<cstdio>
#include<algorithm>
#include<cmath>
int _(){
    int x=0,c=getchar();
    while(c<48)c=getchar();
    while(c>47)x=x*10+c-48,c=getchar();
    return x;
}
int n,m,v[40007],B,id[40007],ts[40007],ans[40007],t1[40007],t2[407],mt[40007],mc[40007];
int*l0[40007],*r0[40007],*l1[40007],*l2[40007],*l3[40007],_mem[160007],*mp=_mem;
struct Q{
    int l,r,k1,k2,ID;
    void init(int i){
        l=_();r=_();k1=_();k2=_();ID=i;
    }
    bool operator<(const Q&w)const{
        return id[l]!=id[w.l]?id[l]<id[w.l]:r!=w.r?(r<w.r)!=(id[l]&1):ID<w.ID;
    }
}q[40007];
void insv(int x,int y){
    if(!t1[x]++)++t2[x/B];
    y=l3[y][x];
    ++l1[x][y];
    ++l2[x][y/B];
}
void delv(int x,int y){
    if(!--t1[x])--t2[x/B];
    y=l3[y][x];
    --l1[x][y];
    --l2[x][y/B];
}
void ins(int x){
    if(ts[x]>0)delv(ts[x],x);
    ++ts[x];
    if(ts[x]>0)insv(ts[x],x);
}
void del(int x){
    if(ts[x]>0)delv(ts[x],x);
    --ts[x];
    if(ts[x]>0)insv(ts[x],x);
}
int rnk(int x,int k){
    for(int i=0,s=0;;++i){
        if(s+l2[x][i]>=k){
            for(int j=i*B;;++j)if((s+=l1[x][j])==k)return l0[x][j];
        }
        s+=l2[x][i];
    }
}
int main(){
    n=_();
    B=sqrt(n+1)+1;
    for(int i=1;i<=n;++i)++mc[++mt[v[i]=_()]],id[i]=(i-1)/B;
    for(int i=1;i<=n;++i){
        l0[i]=r0[i]=mp;mp+=mc[i];
        l1[i]=mp;mp+=mc[i];
        l2[i]=mp;mp+=mc[i];
        l3[i]=mp-1;mp+=mt[i];
    }
    for(int i=1;i<=n;++i)for(int j=1;j<=mt[i];++j)l3[i][j]=r0[j]-l0[j],*r0[j]++=i;
    m=_();
    for(int i=0;i<m;++i)q[i].init(i);
    std::sort(q,q+m);
    int L=1,R=0;
    for(int i=0;i<m;++i){
        int l=q[i].l,r=q[i].r;
        while(L>l)ins(v[--L]);
        while(L<l)del(v[L++]);
        while(R<r)ins(v[++R]);
        while(R>r)del(v[R--]);
        for(int j=0,s=0;;++j){
            if(s+t2[j]>=q[i].k1){
                int k=B*j;
                while(s<q[i].k1)if(t1[k++])++s;--k;
                ans[q[i].ID]=rnk(k,q[i].k2);
                break;
            }
            s+=t2[j];
        }
    }
    for(int i=0;i<m;++i)printf("%d\n",ans[i]);
    return 0;
}

 

posted on 2016-12-11 14:57  nul  阅读(500)  评论(0编辑  收藏  举报