jzoj4438 K小数查询

本文介绍了一种使用分块技术处理大规模数据集的方法,通过将数据分成若干块,并针对每一块应用特定操作,实现高效的查询和更新。适用于数据规模较大且查询频繁的场景。

这里写图片描述

Data Constraint

n<=80000,所有数字的绝对值不超过5000000

Solution

这是一道分块的板题 但是并不是那么好写的
一般来说我们都按照根号n为块的大小进行分块
那么,对于某一个询问或者查找
就对于整块的部分打个标记
而对于边缘部分暴力处理
这道题目的时限给了8s
我们分块之后处理k小数就用二分来解决
这样的复杂度比较玄学

code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int N=80005,sz=300; //sz为块大小
struct edge{
    int s,w,d;
} a[N];
int x,y,z,l,r,n,m,i,mid,j,t;
int s[sz],rr[sz],add[sz];
int read(){
    int sum=0,p=1;
    char c=getchar();
    while (c<'0'||c>'9') {
        if (c=='-') p=-1;
        c=getchar();}
    while (c>='0'&&c<='9') {
        sum=sum*10+c-'0';
        c=getchar();}
    return sum*p;
} 
inline bool cmp(edge a,edge b){ return a.d<b.d;}
inline int search(int h,int k){
    int l,r,mid;
    l=s[h],r=rr[h];
    while (l<r-1){
        mid=(l+r)>>1;
        if (a[mid].d+add[h]<k) l=mid; else r=mid;
    }
    while (a[r].d+add[h]>=k&&r>=l) r--;
    return r-s[h]+1;
}
inline int get(int x,int y,int k){
    int l,r,i,ans;
    ans=0;
    l=(x+sz-1)/sz,r=(y+sz-1)/sz;
    if (l==r){
        fo(i,x,y) if (add[l]+a[a[i].s].d<k) ans++;
        return ans;
    }
    fo(i,l+1,r-1) ans+=search(i,k);
    fo(i,x,rr[l]) if (a[a[i].s].d+add[l]<k) ans++;
    fo(i,s[r],y) if (a[a[i].s].d+add[r]<k) ans++;
    return ans; 
}
int main(){
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    n=read();
    fo(i,1,n) a[i].d=read(),a[i].w=i;
    fo(i,1,(n+sz-1)/sz){
        s[i]=(i-1)*sz+1,rr[i]=min(i*sz,n);
        sort(a+s[i],a+rr[i]+1,cmp);
    }
    fo(i,1,n) a[a[i].w].s=i;//a[i].s为a[i].w的一个反函数
                            //因为我们的数组经过了排序,所以更改x就等于更改a[a[x].s]
    m=read();
    while (m){
        m--;
        t=read(),x=read(),y=read(),z=read();
        if (t==1){
            l=(x+sz-1)/sz,r=(y+sz-1)/sz;
            if (l==r){
                fo(i,x,y) a[a[i].s].d+=z;
                sort(a+s[l],a+rr[l]+1,cmp); 
                //注意这里的排序范围,整一个块都要重新排序,而不是x到y
                fo(i,s[l],rr[l]) a[a[i].w].s=i;
            } else {
                fo(i,l+1,r-1) add[i]+=z;
                fo(i,x,rr[l]) a[a[i].s].d+=z;
                fo(i,s[r],y) a[a[i].s].d+=z;
                sort(a+s[l],a+rr[l]+1,cmp),sort(a+s[r],a+rr[r]+1,cmp);
                fo(i,s[l],rr[l]) a[a[i].w].s=i;
                fo(i,s[r],rr[r]) a[a[i].w].s=i;
            }
        } else {
            l=-5000000,r=5000000; //二分
                while (l<r-1){
                    mid=(l+r)>>1;
                    if (get(x,y,mid)<z) l=mid; else r=mid;
                }
                if (get(x,y,l)<=z) r=l;
                printf("%d\n",r);
            }
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值