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

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

根据数字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. 2010辽宁省赛F(字典树,动态规划)

    #include<bits/stdc++.h>using namespace std;int n,x;char s[10010];char a[31010];int val[100010] ...

  2. FFT求卷积(多项式乘法)

    FFT求卷积(多项式乘法) 卷积 如果有两个无限序列a和b,那么它们卷积的结果是:\(y_n=\sum_{i=-\infty}^\infty a_ib_{n-i}\).如果a和b是有限序列,a最低的项 ...

  3. 老男孩Day1作业(二):三级菜单

    作业需求: 1. 运行程序输出第一级菜单 2. 选择一级菜单某项,输出二级菜单,同理输出三级菜单 3. 菜单数据保存在文件中 4. 让用户选择是否要退出 5. 有返回上一级菜单的功能 1)编写思路 编 ...

  4. POJ3274-Gold Balanced Lineup

    题目链接:点击打开链接 Gold Balanced Lineup Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16978 ...

  5. thinkphp5命令行访问

    入口文件后加一个空格就行了 1,首先cd到站点目录public下,我的入口文件是默认的index.php,然后执行以下命令, 2,php要加入环境变量 访问index模块下的index控制器下的tes ...

  6. EcmaScript源码

    /** @type {Number} @const */ NaN = 0; /** @type {Number} */ Infinity = 0; undefined = 0; /** @param ...

  7. github:当你想要使用VSCODE开心提交代码时,出现Git:git@github.com:Permission denied(publickey)解决方案

    当你想要使用VSCODE开心提交代码时,出现Git:git@github.com:Permission denied(publickey)弹框 图片: 原因:电脑公钥(publickey)未添加至gi ...

  8. 查询rabbitmq

    package com.yunda.app.service; import java.io.InputStream; import java.net.HttpURLConnection; import ...

  9. SVN服务器地址更换方法

    由于工作需要,已将SVN服务器从172.16.8.xxx上迁移至172.16.8.yyy上,SVN地址变为:https://172.16.8.yyy:8443/svn,原下载到客户端电脑的svn不需要 ...

  10. 035 Search Insert Position 搜索插入位置

    给定一个排序数组和一个目标值,如果在数组中找到目标值则返回索引.如果没有,返回到它将会被按顺序插入的位置.你可以假设在数组中无重复元素.案例 1:输入: [1,3,5,6], 5输出: 2案例 2:输 ...