[POJ 2104] K-th Number (块状数组)

POJ - 2104
给定一个长度为 N的互不相同的序列和 M个询问
每次询问给出一个区间,问区间内第 K大的数是多少

分块做法:
将区间分块,每块大小 sqrt(n),分块内排序
每次查询一个第 K大,先二分确定这个数是多少
然后在区间内统计小于他的数有多少个,如果 cnt>=K,则缩小这个数 (log(n))
统计方式是,每次查询的一个区间
如果完全包含了一个分块,则在分块内二分搜索 (n/sqrt(n)=sqrt(n))
而不完全包含分块的部分,则暴力地一个个查找 (sqrt(n))
时间复杂度 O(m*log(n)*sqrt(n))

分块第一题,说一下感想:
1) 要注意分块的时候,右边界的处理,他可能会超过 N,要判断下
2) 关于为什么块的大小是 bsiz=sqrt(n)
很显然,每次查询的复杂度是 n/bsiz + bsiz,由基本不等式可得
当 bsiz=sqrt(n)的时候,这个复杂度有最小值 sqrt(n)

关于这题的一些坑点:
1) 二分查找的时候,不是在 [-INF, INF]范围查找,而是对原数组排序,在原数组里查找值
2) 这题分块的大小要强制限定为 1000,不要问我为啥不是 sqrt(n),因为玄学

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)

const int maxn=1e5+10,INF=1e9+10;
int N,M,bsiz;
int inpt[maxn];
int sarr[maxn];
int arry[maxn];

template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch))  {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

int Cnt(int,int,int);

int main()
{
//  #ifdef LOCAL
//  freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
//  #endif
    while(~scanf("%d%d", &N, &M))
    {
        for(int i=0; i<N; i++)
        {
            scanf("%d", &inpt[i]);
            sarr[i]=inpt[i];
        }
        sort(sarr,sarr+N);
//      bsiz=sqrt(N);
        bsiz=1000;
        int front=0;
        while(front<N)
        {
            int rear=front+bsiz;
            if(rear>=N) rear=N;
            for(int i=front; i<rear; i++) arry[i]=inpt[i];
            sort(arry+front,arry+rear);
            front=rear;
        }
        int x,y,k;
        for(int i=1; i<=M; i++)
        {
            scanf("%d%d%d", &x, &y, &k);
            int l=0,r=N-1;
            while(l<r)
            {
                int mid=(l+r+1)>>1;
                if(Cnt(x-1,y,sarr[mid])>=k) r=mid-1;
                else l=mid;
            }
            printf("%d\n", sarr[l]);
        }
    }
    return 0;
}

int Cnt(int ql, int qr, int num)
{
    int np=ql,tcnt=0;
    while(np<qr)
    {
        int pos=np/bsiz,bl=pos*bsiz,br=pos*bsiz+bsiz;
        if(ql<=bl&&br<=qr) tcnt+=lower_bound(arry+bl,arry+br,num)-(arry+bl);
        else
        {
            int l=max(ql,bl),r=min(br,qr);
            for(int i=l; i<r; i++)
            {
                if(inpt[i]<num) tcnt++;
            }
        }
        np=br;
    }
    return tcnt;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值