POJ 2104 K-th Number(分桶,线段树,主席树)
一道比较经典的数据结构题。可以用多种方式来做。
一,分桶法(平方分解)。
根据数字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(分桶,线段树,主席树)的更多相关文章
- POJ 2104:K-th Number(整体二分)
http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- 洛谷P3834 可持久化线段树(主席树)模板
题目:https://www.luogu.org/problemnew/show/P3834 无法忍受了,我要写主席树! 解决区间第 k 大查询问题,可以用主席树,像前缀和一样建立 n 棵前缀区间的权 ...
- POJ 2104:K-th Number(主席树静态区间k大)
题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var ...
- 静态区间第k大(分桶法和平方分割)
POJ 2104为例 思想: <挑战程序设计竞赛>中介绍的方法. 分桶法:把一排物品或者平面分成桶,每个桶分别维护自己内部的信息,已达到高效计算的目的. 设一共有n个数,每b个分到一个桶里 ...
- POJ 2104:K-th Number 整体二分
感觉整体二分是个很有趣的东西. 在别人的博客上看到一句话 对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了 树套树写了一天还是WA着,调得焦头烂额,所以决定学cd ...
- 【POJ 2104】 K-th Number 主席树模板题
达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没 ...
- [POJ 2104]K-th Number【模板】(主席树)
题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...
- [POJ2104] K – th Number (可持久化线段树 主席树)
题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...
随机推荐
- linux 内核与用户空间通信机制netlink初探
1.Linux进程概述 Linux中的进程间通信机制源自于Unix平台上的进程通信机制.Unix的两大分支AT&T Unix和BSD Unix在进程通信实现机制上各有所不同,前者形成了运行 ...
- Go:Nsq消息队列
Nsq服务端简介 在使用Nsq服务之前,还是有必要了解一下Nsq的几个核心组件整个Nsq服务包含三个主要部分 nsqlookupd 先看看官方的原话是怎么说:nsqlookupd是守护进程负责管理拓扑 ...
- Python-OpenCV中图像颜色空间转换
目录 cv2.cvtColor() 1. RGB to GRAY 2. RGB to CIE XYZ 3. RGB to YCrCb JPEG 4. RGB to HSV 5. RGB to HLS ...
- 从零开始安装 Ambari (2) -- 准备本地 repository
安装 Ambari,最后是为了用它部署 hadoop 集群.安装时默认使用的是 hortonworks 远程的资源,用它部署集群时,需要下载 Hadoop.Hive.HBase 的安装包,速度很慢.我 ...
- bzoj3731: Gty的超级妹子树(树分块)
传送门 分块树,代码参考了Manchery的 具体细节还是看代码好了 这题卡常……注意常数写好点…… //minamoto #include<iostream> #include<c ...
- springboot 简单使用shiro登录
首先引入需要的pom <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shir ...
- Proxy opening connection toSpringClound配置豪猪hystrixDashboard发生
Proxy opening connection to: http://localhost:8001/hystrix.stream 配置hystrixDashboard发生的错误.一直在寻找 最后发现 ...
- BZOJ 1053 [HAOI2007]反素数ant 神奇的约数
本蒟蒻终于开始接触数学了...之前写的都忘了...忽然想起来某神犇在几个月前就切了FWT了... 给出三个结论: 1.1-N中的反素数是1-N中约数最多但是最小的数 2.1-N中的所有数的质因子种类不 ...
- scala数据类型
# Scala数据类型 ## 1.数值类型 ### 1.1 与Java一样Scala也有8种数值类型 * Byte * Char * Short * Int * Long * Float * Doub ...
- GUI的最终选择 Tkinter(五):Text用法
Text组件 绘制单行文本使用Label组件,多行选使用Listbox,输入框使用Entry,按钮使用Button组件,还有Radiobutton和Checkbutton组件用于提供单选或多选的情况, ...