【HNOI2016模拟4.10】 K小数查询

本文介绍了一种使用分块算法解决区间加法与查询第k小数值的问题。通过将数据分块并结合线段树等数据结构,有效地解决了区间操作带来的挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

维护一个长度为n的序列,该序列支持Q个操作:
①将第L到R个数加上x
②询问L到R之间第k小个数是什么。

Solve

看到实现时间7000ms,嘿嘿,果断分块打法。~~

大呼:分块大法好

这题因为有区间加操作,直接用数据结构难以维护区间 K 小值。但是可以用
分块解决此题。最简单的方法是每个块维护原块和排序后的块,询问时二分答案,
话说这时间复杂度有点玄学啊。。汗。。(⊙﹏⊙)
也可用分块套线段树但常数较大。 蒟蒻表示Σ( ° △ °|||)︴
“本题数据范围对于分块题来说较大,时间较长,是为了区分分块和暴力。”——题解

这是什么鬼。。好几次吓得我以为超时了。。

Code

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define INF 5000000
#define N 80005
#define L(x) (x-1)*block+1
#define R(x) min(x*block,n)
using namespace std;

int n,block,a[N],b[N],wz[N],c[N],MAX=0;
void Px(int x)
{
    fo(i,L(x),R(x)) b[i]=a[i];
    sort(b+L(x),b+R(x)+1);
}
void Refresh(int l,int r,int x)
{
    if (wz[l]==wz[r])
    {
        fo(i,l,r) a[i]+=x;
        Px(wz[l]);
    }
    else
    {
        fo(i,wz[l]+1,wz[r]-1) c[i]+=x;
        fo(i,l,R(wz[l])) a[i]+=x; Px(wz[l]);
        fo(i,L(wz[r]),r) a[i]+=x; Px(wz[r]);
    }
}
int Find(int l,int r,int x)
{
    int ans=0;
    if (wz[l]==wz[r]) fo(i,l,r) ans+=(a[i]+c[wz[i]]<=x);
    else
    {
        fo(i,wz[l]+1,wz[r]-1) ans+=upper_bound(b+L(i),b+R(i)+1,x-c[i])-(b+L(i));
        fo(i,l,R(wz[l])) ans+=(a[i]+c[wz[i]]<=x);
        fo(i,L(wz[r]),r) ans+=(a[i]+c[wz[i]]<=x);
    }
    return ans;
}
int Binary_search(int ll,int rr,int x)
{
    int l=-INF,r=INF;
    while (l<r-1)
    {
        int mid=(l+r)/2;
        if (Find(ll,rr,mid)<x) l=mid;
        else r=mid;
    }
    return r;
}
int main()
{
    freopen("data.in","r",stdin);
    freopen("4438.out","w",stdout);
    scanf("%d",&n);
    block=sqrt(n);
    int m=n/block+(n%block?1:0);
    fo(i,1,n)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
        wz[i]=(i-1)/block+1;    
    }
    fo(i,1,m) sort(b+L(i),b+R(i)+1);
    int Q;
    scanf("%d",&Q); 
    int mark,l,r,x;
    while (Q--)
    {
        scanf("%d%d%d%d",&mark,&l,&r,&x);
        if (mark==1) Refresh(l,r,x);
        else printf("%d\n",Binary_search(l,r,x));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值