一道比较经典的数据结构题。可以用多种方式来做。

一,分桶法(平方分解)。

根据数字x的大小和区间内不大于x的数字数量cnt的单调性,可知第k大数kth对应的cnt应该满足cnt≥k,

且kth是满足条件的最小的一个,可以二分下界。

关键在于高效找出cnt,对于每个完整的桶,排序以后二分,不完整的桶就直接暴力找。

桶的大小设置为B,那么查询复杂度为O(n/B*log(B) + B)。 由于每个桶的不是O(1),需适当减小桶数,

最平衡取法是令:n/B*logB = B。可以得到,B≤sqrt(n logn)。 这里log确切的常数是log2。

我测试了一下,最后取的是B =  floor(sqrt(n*log(n)))  1072。

桶我用的vector保存,据说,vector的capacity()不够的时候是倍增的,比较耗内存,复杂度是Θ(nlogn)的,

如果一开始就reserve()确实会快一些。

预处理O(nlogn),查询O(m*logn*sqrt(nlogn))

kb         ms                                  length

/*********************************************************
* ------------------ *
* author AbyssalFish *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
using namespace std; typedef long long ll; const int B = ;//1072 11047ms, 1000 11563ms, 1288.98 11657ms
const int maxn = 1e5+; int n, m;
int a[maxn], num[maxn]; #define PB push_back
#define all(x) x.begin(), x.end() vector<int> buk[maxn/B+]; //#define LOCAL
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif
//int n = 1e5;
//cout<<sqrt(n*log2(n))<<' '<<sqrt(n)<<' '<<sqrt(n*log(n));
//cout<<buk[0].capacity();
for(int i = maxn/B; i--; ) { buk[i].reserve(B+); } // 11047 -> 10969ms
scanf("%d%d",&n,&m);
for(int i = ; i < n; i++){
scanf("%d", a+i);
buk[i/B].PB(a[i]);
}
memcpy(num,a,sizeof(int)*n);
sort(num,num+n);
//(n-1)/b <= n/b 当 n%b != 0时候取等号,这时候不是一个完整的桶,所以最后一个桶不sort也没关系
for(int i = n/B; i--;) sort(all(buk[i]));
while(m--){
int l,r,k; scanf("%d%d%d",&l,&r,&k);
int lb = , ub = n-;
while(lb < ub){
int md = (lb+ub)>>;
int x = num[md];
int p = l-, q = r, c = ;
while(p<q && p%B) if(a[p++] <= x) c++;
while(p<q && q%B) if(a[--q] <= x) c++;
for(int i = p/B, mi = q/B; i < mi; i++){
c += upper_bound(all(buk[i]),x)-buk[i].begin();
}
c >= k?ub = md: lb = md+;
}
printf("%d\n", num[lb]);
} return ;
}

2. 线段树

思路同上,只是计算cnt的时候改用线段树。线段树每个结点保存有序的数组,建树过程是归并排序的完整再现,因此也叫归并树。

更抽象来看,数值大小和位置是两个维度,查询就是询问一个空间内的点数,也就是所谓的区域树了,通过嵌套可以推广到高维。

建树O(nlogn), 查询O(m*logn^2),树状数组二分也是可以的。

kb           ms                                length

/*********************************************************
* ------------------ *
* author AbyssalFish *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
using namespace std; typedef long long ll; #define para int d = 0, int l = 0,int r = n
#define TEMPvar int mid = (l+r)>>1;
#define lsn d+1, l, mid
#define rsn d+1, mid, r
#define insd ql<=l&&r<=qr
const int maxn = 1e5;
int n, m;
int a[maxn];
const int Log2N_ = ; int dat[Log2N_][maxn]; void build(para)
{
if(r-l == ){
dat[d][l] = a[l];
}else {
TEMPvar
build(lsn);
build(rsn);
merge(dat[d+]+l,dat[d+]+mid,dat[d+]+mid,dat[d+]+r,dat[d]+l);
}
} int ql, qr, qval;
int query(para)
{
if(insd){
return upper_bound(dat[d]+l,dat[d]+r,qval) - dat[d]-l;
}else { ////intersect
int res = ;
TEMPvar
if(ql<mid) res += query(lsn);
if(qr>mid) res += query(rsn);
return res;
}
} //#define LOCAL
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif
//cout<<(int)ceil(log2(maxn))+1;
scanf("%d%d",&n,&m);
for(int i = ; i < n; i++){
scanf("%d", a+i);
}
build();
while(m--){
int k; scanf("%d%d%d",&ql,&qr,&k); ql--;
int lb = , ub = n-;
while(lb < ub){
int md = (lb+ub)>>;
qval = dat[][md];
query() >= k? ub = md:lb = md+;
}
printf("%d\n", dat[][lb]);
} return ;
}

3. 主席树。

主席树建树的思想就是可持续化。范围下标是值域,具有前缀性,只要维护结点数量就可以直接查询kth。

建树O(nlogn), 查询O(m*logn)。比较费内存。

kb          ms                                length

/*********************************************************
* ------------------ *
* author AbyssalFish *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
using namespace std; typedef long long ll; #define TEMPvar int mid = (l+r)>>1;
#define lsn o->lc, l, mid
#define rsn o->rc, mid+1, r
const int maxn = 1e5+;
int a[maxn], rk[maxn], ith[maxn];//ith[i]是第i大元素的下标 rk[i]是i号元素的名次
const int ST_SIZE = *maxn;
struct Node
{
Node* lc, *rc;
int s;
}nd[ST_SIZE];
Node * const nil = nd;
Node *rt[maxn];
int cnt;
inline Node* newNode(Node *old)
{
Node *nw = nd+(++cnt);
*nw = *old;
return nw;
} int qval;
void inst(Node *&o,int l,int r)
{
o = newNode(o);
o->s++;
if(l == r) return;
TEMPvar
if(qval <= mid) inst(lsn);
else inst(rsn);
} int query(Node *a,Node *b,int l,int r,int k)
{
if(l==r) return l;
TEMPvar
int ts = b->lc->s - a->lc->s;
if(ts >= k) return query(a->lc,b->lc,l,mid,k);
else return query(a->rc,b->rc,mid+,r,k-ts);
}
//[](int i,int j){ return a[i] < a[j];}
bool cmp(int i,int j){ return a[i] < a[j];}
//#define LOCAL
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif
//cout<<(int)ceil(log2(maxn))+1;
int n,m; scanf("%d%d",&n,&m);
for(int i = ; i < n; i++) {
scanf("%d", a+i);
ith[i] = i;
}
sort(ith,ith+n,cmp);
for(int i = ; i < n; i++) rk[ith[i]] = i;
rt[] = nil;
*nil = {nil,nil,};
for(int i = ; i <= n; i++){
rt[i] = rt[i-];
qval = rk[i-];
inst(rt[i],,n-);
}
while(m--){
int i,j,k; scanf("%d%d%d",&i,&j,&k);
printf("%d\n", a[ith[query(rt[i-],rt[j],,n-,k)]]);
}
return ;
}

POJ 2104 K-th Number(分桶,线段树,主席树)的更多相关文章

  1. POJ 2104:K-th Number(整体二分)

    http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...

  2. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  3. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  4. 洛谷P3834 可持久化线段树(主席树)模板

    题目:https://www.luogu.org/problemnew/show/P3834 无法忍受了,我要写主席树! 解决区间第 k 大查询问题,可以用主席树,像前缀和一样建立 n 棵前缀区间的权 ...

  5. POJ 2104:K-th Number(主席树静态区间k大)

    题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...

  6. 静态区间第k大(分桶法和平方分割)

    POJ 2104为例 思想: <挑战程序设计竞赛>中介绍的方法. 分桶法:把一排物品或者平面分成桶,每个桶分别维护自己内部的信息,已达到高效计算的目的. 设一共有n个数,每b个分到一个桶里 ...

  7. POJ 2104:K-th Number 整体二分

    感觉整体二分是个很有趣的东西. 在别人的博客上看到一句话 对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了 树套树写了一天还是WA着,调得焦头烂额,所以决定学cd ...

  8. 【POJ 2104】 K-th Number 主席树模板题

    达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没 ...

  9. [POJ 2104]K-th Number【模板】(主席树)

    题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

  10. [POJ2104] K – th Number (可持久化线段树 主席树)

    题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

随机推荐

  1. Python中的变量,数据类型

    Python中变量的命名规则:以字母和下划线开头,由字母,数字和下划线组成,区分大小写 Python中同样有加减乘除取余运算,还有一个运算符**,相当与幂运算,当然,幂运算的优先级要高于加减乘除 最后 ...

  2. Spring MVC 基于Method的映射规则(注解版)

    在Restful风格的web开发中,根据不同的请求方法使用相应的控制器处理逻辑成为核心需求,下面就看看如何在Spring MVC中识别不同的请求方法. 请求方法 在Http中,请求的方法有很多种,最常 ...

  3. 异常HttpMessageNotWritableException解决办法

    1. 问题描述 在写SpringBoot项目的时候,由于是前后端分离项目,为了统一接口形式,使用一个类封装了返回数据的形式,但是在测试的时候报异常HttpMessageNotWritableExcep ...

  4. POJ2388-Who's in the Middle

    题目链接:点击打开链接 Who's in the Middle Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 45683   ...

  5. Migration-添加表

    public partial class _111111 : DbMigration { public override void Up() { CreateTable( "dbo.Asse ...

  6. 026 Remove Duplicates from Sorted Array 从排序数组中删除重复项

    给定一个有序数组,你需要原地删除其中的重复内容,使每个元素只出现一次,并返回新的长度.不要另外定义一个数组,您必须通过用 O(1) 额外内存原地修改输入的数组来做到这一点.示例:给定数组: nums ...

  7. android 缓存路径

    用程序在运行的过程中如果需要向手机上保存数据,一般是把数据保存在SDcard中的.大部分应用是直接在SDCard的根目录下创建一个文件夹,然后把数据保存在该文件夹中.这样当该应用被卸载后,这些数据还保 ...

  8. <linux下内置命令和外部命令>

    Linux下内置命令和外部命令 1.linux的命令可以分为内部命令和外部命令: 内部命令在系统启动时就调入内存,是常驻内存的,所以执行效率高. 而外部命令是系统的软件功能,用户需要时才从硬盘中读入内 ...

  9. 《zabbix监控的搭建》centos5.8 32

    系统环境centos5.8 32位操作系统   这里以zabbix-2.2.7为例: 下载官方的软件包: http://pan.baidu.com/s/1ntuTRYh 官方的参考文档: https: ...

  10. java多线程处理任务

    最近用到使用多线程处理给用户发送站内消息的问题,想到使用java自带的线程池进行处理这个问题,具体如下: 定义一个线程: package com.qlwb.util; import org.apach ...