【OJ2130】K小数查询

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);
        }
    }
}

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

时间: 2024-10-27 16:08:46

【OJ2130】K小数查询的相关文章

AcWing 255. 第K小数 (主席树写法)

区间k小数是主席树的模板题目,如果区间不包含,用莫队+权值线段树也能解 主席树是可持久化线段树,所为可持久化,就是每次只新增不一样的节点,而保留前面的版本,这样可以做到查询. 如果询问时1-r,那么直接主席树,询问的是l-r,就用到前缀和思想,具体看代码注释 #include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #inclu

BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

关于利用快排思想求第K小数的分析

最近复习快排算法,记得当时最有意思的是可以用快排的partition函数求出第K小数 于是上网搜索一番,发现都只是贴出了代码.无奈只好自己研究了下 利用快排partition求第k小数不得不从partition函数开始说: 快速排序的思想是在一组待排序的数中,找出一个数作为分界,使得它前面的数都比它小,后面的数都比它大.这个数叫做枢轴 当求出一组数的枢轴以后,一组数就可以以枢轴为界限分成两组,我们可以递归的找出这两个组的枢轴,只到每组只有一个数 这里我们应该注意的是,partition函数返回的

C语言 &#183; 区间K大数查询

问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. 第二行包含n个正整数,表示给定的序列. 第三个包含一个正整数m,表示询问个数. 接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个.序列元素从1开始标号. 输出格式 总共输出m行,每行一个数,表示询问的答案. 样例输入 51 2 3 4 521 5 22 3 2 样例输出 42 数据规模与约定 对于30%的数据,n,m<=1

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

Bsoj 1322 第K小数

第K小数 Description 现在已有N个整数,你有以下三种操作: 1 A:表示加入一个值为A的整数: 2 B:表示删除其中值为B的整数: 3 K:表示输出这些整数中第K小的数: Input 第一行,两个整数N,M,表示最开始有N个整数,总共有M个操作 第二行用空格隔开的N个整数 接下来M行,每行表示一个操作 Output 若干行,一行一个整数,表示所求的第K小的数字 Sample Input 5 5 6 2 7 4 9 1 8 1 6 3 10 2 4 3 3 Sample Output

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

hiho week 37 P1 : 二分&#183;二分查找之k小数

P1 : 二分·二分查找之k小数 Time Limit:10000ms Case Time Limit:1000ms Memory Limit:256MB 描述 在上一回里我们知道Nettle在玩<艦これ>,Nettle的镇守府有很多船位,但船位再多也是有限的.Nettle通过捞船又出了一艘稀有的 船,但是已有的N(1≤N≤1,000,000)个船位都已经有船了.所以Nettle不得不把其中一艘船拆掉来让位给新的船.Nettle思考了很久, 决定随机选择一个k,然后拆掉稀有度第k小的船. 已知

【COGS 1534】 [NEERC 2004]K小数 &amp;&amp;【COGS 930】 [河南省队2012] 找第k小的数 可持久化01Trie

板子题,只是记得负数加fix最方便 #include <cstdio> const int A=19,N=100010; namespace FIFO { char ch,B[1<<20],*S=B,*T=B; #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<20,stdin),S==T)?0:*S++) #define isd(c) (c>='0'&&c<='9') int aa,bb