OJ2130K小数查询

Posted farway17

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OJ2130K小数查询相关的知识,希望对你有一定的参考价值。

2130 -- K小数查询(Solution)

题目大意 : 给你一个长度为 (N) 的数列和 (Q) 个操作,操作包括:①区间加一个数;②询问区间内第 (k) 小的数。((n,qle80000)) .

Tag: 二分、分块

Analysis By LC:

我知道这题可以用主席树做,但像我这么菜的咸鱼选手怎么可能会主席树。所以我们用分块。

第一个操作时分块常规操作,但第二个看起来不是很好做。我们可以尝试用二分答案转化问题求解,:一个数有至少 (k-1) 个数比它小,该数最小值即为所求。那么我们只需对于每个块,找出有多少个数比该数小就行了。我们可以维护每一块排序后的数列,查询时仅需用STL的 (bound) 系列查找函数即可。

虽然是分块套路题,但个人认为细节还是不少,可能是我太菜了吧。

Code By LC :

#include<cstdio>
#include<algorithm>
using namespace std;
inline int _read()
{
    char c; int x=0,f=1;
    for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    return x*f;
}
const int N=80005,INF=5000000;
int n,a[N],ind,l[N],r[N],id[N],ta[N],isl[N],pos[N];
struct node
{
    int id,w;
    bool operator < (const node x) const {
        return w < x.w;
    }
} s[N];
void rebuild(int x)
{
    sort(s+l[x],s+1+r[x]);
    for(int i=l[x];i<=r[x];i++)
        pos[s[i].id]=i;
}
bool check(int mid, int ql, int qr, int x)
{
    int k=0,i;
    for(;ql<=qr&&!isl[ql];ql++)k+=(s[pos[ql]].w+ta[id[ql]]<=mid);
    if(ql>n||ql>qr) return k>=x;
    for(i=isl[ql];i<=ind&&r[i]<=qr;i++)
    {
        int tmp=upper_bound(s+l[i],s+1+r[i],(node){-1,mid-ta[i]})-s;
        k+=tmp-l[i];
    }
    if(i>ind) return k>=x;
    for(ql=l[i];ql<=qr;ql++)k+=(s[pos[ql]].w+ta[id[ql]]<=mid);
    return k>=x;
}
int main()
{
    n=_read();
    const int m=sqrt(n);
    for(int i=1;i<=n;i=min(n,i+m-1)+1)
    {
        l[++ind]=i,r[ind]=min(n,i+m-1);
        isl[i]=ind;
    }
    for(int i=1;i<=ind;i++)
        for(int j=l[i];j<=r[i];j++) id[j]=i;
    for(int i=1;i<=n;i++) s[i]=(node){i,_read()};
    for(int i=1,k=0;i<=ind;i++) sort(s+l[i],s+1+r[i]);
    for(int i=1;i<=n;i++) pos[s[i].id]=i;
    int q=_read();
    while(q--)
    {
        int op=_read(),ql=_read(),qr=_read(),x=_read();
        if(op==1)
        {
            int i;
            for(;ql<=qr&&!isl[ql];ql++) s[pos[ql]].w+=x;
            rebuild(id[ql-1]);
            if(ql>qr) continue;
            for(i=isl[ql];i<=ind&&r[i]<=qr;i++)ta[i]+=x;
            if(i<=ind) for(ql=l[i];ql<=qr;ql++) s[pos[ql]].w+=x;
            rebuild(i);
        }
        if(op==2)
        {
            int tl=-INF,tr=INF;
            while(tl<=tr)
            {
                int mid=tl+tr>>1;
                if(check(mid,ql,qr,x)) tr=mid-1;
                else tl=mid+1;
            }
            printf("%d
",tr+1); 
        }
    }
}

以上是关于OJ2130K小数查询的主要内容,如果未能解决你的问题,请参考以下文章

OJ 第m小数

OJ2237第k小数题解

值域线段树 (玲珑OJ 1117)

九度OJ小结

九度oj 题目1397:查找数段

#OJ没有一次AC题解01-010309