K小数查询

题目大意

兹瓷区间加和区间求k小。

分块大法好

我们将序列分块,每一块维护add标记和排序后的序列。
对于区间加,跨过整块就直接打标记,多余部分暴力修改然后重构所在块。
对于区间求k小,显然可以二分答案,然后转化为判定问题。
在一个块内求

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=80000+10,maxc=690+10,maxd=5000000;
int belong[maxn],add[maxn],a[maxn],size[maxn/maxc+10];
int b[maxn/maxc+10][maxc];
int i,j,k,l,r,mid,t,n,m,c,x;
void build(int x){
    int i;
    size[x]=0;
    fo(i,(x-1)*c+1,min(x*c,n)) b[x][++size[x]]=a[i];
    sort(b[x]+1,b[x]+size[x]+1);
}
int binary(int x,int y){
    int l=0,r=size[x],mid;
    while (l<r){
        mid=(l+r+1)/2;
        if (b[x][mid]+add[x]<y) l=mid;else r=mid-1;
    }
    return l;
}
int get(int j,int k,int x){
    int i,ans=0;
    int l=belong[j],r=belong[k];
    if (l==r){
        fo(i,j,k) 
            if (a[i]+add[l]<x) ans++;
        return ans;
    }
    fo(i,l+1,r-1) ans+=binary(i,x);
    fo(i,j,l*c) 
        if (a[i]+add[l]<x) ans++;
    fo(i,(r-1)*c+1,k)
        if (a[i]+add[r]<x) ans++;
    return ans;
}
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int main(){
    freopen("kth2.in","r",stdin);freopen("kth.out","w",stdout);
    n=read();
    c=690;
    fo(i,1,n){
        a[i]=read();
        belong[i]=(i-1)/c+1;
    }
    fo(i,1,belong[n]) build(i);
    m=read();
    while (m--){
        t=read();j=read();k=read();x=read();
        if (t==1){
            l=belong[j];r=belong[k];
            if (l==r){
                fo(i,j,k) a[i]+=x;
                build(l);
                continue;
            }
            fo(i,l+1,r-1) add[i]+=x;
            fo(i,j,l*c) a[i]+=x;
            fo(i,(r-1)*c+1,k) a[i]+=x;
            build(l);build(r);
        }
        else{
            l=-maxd;r=maxd;
            while (l<r){
                mid=(l+r+1)/2;
                if (get(j,k,mid)<=x-1) l=mid;else r=mid-1;
            }
            printf("%d\n",l);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值