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