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

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

根据数字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. 《OD学hadoop》20160903某旅游网项目实战

    一.大数据的落地点 1.数据出售 数据商城:以卖数据为公司的核心业务 2. 数据分析 百度统计 友盟 GA IBM analysis 3.搜索引擎 4. 推荐系统 mahout 百分比 5.精准营销 ...

  2. 三层登录——VB.NET版

    前言 由于下面的机房收费系统重构自己要用VB.NET进行重构,所以在敲三层登录的时候,实践了一份C#版三层登录,接着就是VB.NET版的三层登录.话说还有七层登录,一下子感觉三层又矮小了.万丈高楼平地 ...

  3. 洛谷P2055 [ZJOI2009]假期的宿舍

    P2055 [ZJOI2009]假期的宿舍 题目描述 学校放假了 · · · · · · 有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如 A 和 B 都是学校的学生,A ...

  4. C#线程入门(一)

    入门 概述与概念 创建和开始使用多线程 线程同步基础 同步要领 锁和线程安全 Interrupt 和 Abort 线程状态 等待句柄 同步环境 使用多线程 单元模式和Windows Forms Bac ...

  5. CF616D Longest k-Good Segment

    题目描述 给定一个包含\(n\)个整数的序列\(a\),\(0\le a_i \le 10^6\),询问不重复数字个数\(\le k\)的最长区间的左右端点.如果有多解输出任意一组. 输入输出格式 输 ...

  6. HTTP的一些理解

    URI是Uniform Resource Identifier的缩写,统一资源标识符.URI用字符串标识某一互联网资源,而URL标识资源的地点(互联网上所处的位置).可见URL是URI的子集. 典型的 ...

  7. CodeForces - 186A-Comparing Strings

    Some dwarves that are finishing the StUDY (State University for Dwarven Youngsters) Bachelor courses ...

  8. Java 中常用的数据源

    数据源:存储了所有建立数据库连接的信息.就象通过指定文件名你可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接. 1.JNDI方式创建DataSource 1.1 配置 ...

  9. maven插件: shade, assembly

    shade插件的作用: 通过版本的exclution无法解决jar冲突的问题, 解决方案是把依赖的包打到本model的jar中,打包的时候由mvn plugin自动修改代码中的依赖jar包名 relo ...

  10. 练习二十二:python兵乓求比赛顺序练习,关于连个兵乓球队进行比赛

    已知有两支兵乓球队进行比赛,每队各出3人: 甲队有a,b,c三人,乙队有x,y,z三人,已抽签决定比赛名单 问题:有人向队员打听比赛名单.a说他不和X比,c说他不和x,z比,程序找出比赛对手 方法一: ...