首页 > 其他 > 详细

【OJ2130】K小数查询

时间:2018-08-03 23:38:53      阅读:302      评论:0      收藏:0      [点我收藏+]

2130 -- K小数查询(Solution)

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

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\n",tr+1); 
        }
    }
}

【OJ2130】K小数查询

原文:https://www.cnblogs.com/farway17/p/9416875.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!