ZOO

题目大意

M个询问询问区间K小值。

整体二分

我们用K大数查询这道题的思路整体二分。
这道题直接套用K大数查询需要带两个log。
我们把询问l~r拆为两个询问1~r和1~l-1,那么每个询问都变成一个前缀,于是我们想着搞一波线性作法。
观察到对于答案区间为l~r,我们只需要扫描在这个区间内的数。
也就是说对于l~r中的数,<=mid的数继续归到左区间其余归到右区间。
一个区间内的询问按照右端点排序,然后就可以线性做。
总复杂度n log n。
注意本题答案范围比较大故先进行离散化。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100000+10;
struct dong{
    int a,cnt,x;
    bool p;
};
dong ask[maxn*2];
int h1[maxn*5],h2[maxn*5],h3[maxn*5],la1[maxn*5],la2[maxn*5],la3[maxn*5],n1[maxn*20],n2[maxn*20],n3[maxn*20],g1[maxn*20],g2[maxn*20],g3[maxn*20];
int a[maxn],h[maxn],ans[maxn],rank[maxn],id[maxn*2];
int i,j,k,l,t,n,m,t1,t2,t3;
//h1=dl h2=ld h3=que
void add1(int x,int y){
    g1[++t1]=y;
    n1[la1[x]]=t1;
    la1[x]=t1;
    if (!h1[x]) h1[x]=t1;
}
void add2(int x,int y){
    g2[++t2]=y;
    n2[la2[x]]=t2;
    la2[x]=t2;
    if (!h2[x]) h2[x]=t2;
}
void add3(int x,int y){
    g3[++t3]=y;
    n3[la3[x]]=t3;
    la3[x]=t3;
    if (!h3[x]) h3[x]=t3;
}
void solve(int p,int l,int r){
    int j,i,k,t,num=0;
    if (l==r){
        t=h3[p];
        while (t){
            j=g3[t];
            ans[j]=h[l];
            t=n3[t];
        }
        return;
    }
    int mid=(l+r+1)/2;
    i=h2[p];k=h1[p];
    while (i){
        t=g2[i];
        while (k){
            j=g1[k];
            if (ask[j].a>=t) break;
            ask[j].x=num;
            k=n1[k];
        }
        if (a[t]<=mid-1) num++;
        i=n2[i];
    }
    while (k){
        j=g1[k];
        ask[j].x=num;
        k=n1[k];
    }
    i=h3[p];
    while (i){
        t=g3[i];
        if (ask[t*2].cnt+ask[t*2].x-ask[t*2-1].cnt-ask[t*2-1].x<=rank[t]-1){
            ask[t*2].cnt+=ask[t*2].x;
            ask[t*2-1].cnt+=ask[t*2-1].x;
            ask[t*2].x=ask[t*2-1].x=0;
            ask[t*2-1].p=ask[t*2].p=1;
            add3(p*2+1,t);
        }
        else{
            ask[t*2].x=ask[t*2-1].x=0;
            ask[t*2-1].p=ask[t*2].p=0;
            add3(p*2,t);
        }
        i=n3[i];
    }
    i=h1[p];
    while (i){
        t=g1[i];
        if (ask[t].p) add1(p*2+1,t);else add1(p*2,t);
        i=n1[i];
    }
    i=h2[p];
    while (i){
        t=g2[i];
        if (a[t]<=mid-1) add2(p*2,t);else add2(p*2+1,t);
        i=n2[i];
    }
    solve(p*2,l,mid-1);
    solve(p*2+1,mid,r);
}
bool cmp(int a,int b){
    return ask[a].a<ask[b].a;
}
int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        scanf("%d",&a[i]);
        h[i]=a[i];
    }
    sort(h+1,h+n+1);
    fo(i,1,n) a[i]=lower_bound(h+1,h+n+1,a[i])-h,add2(1,i);
    fo(i,1,m){
        scanf("%d%d%d",&j,&k,&l);
        rank[i]=l;
        add3(1,i);
        ask[i*2-1].a=j-1;
        ask[i*2].a=k;
    }
    fo(i,1,m*2) id[i]=i;
    sort(id+1,id+m*2+1,cmp);
    fo(i,1,m*2) add1(1,id[i]);
    solve(1,1,n);
    fo(i,1,m) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值