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小值. 输入输 ...
随机推荐
- [CentOS7] ssh免密登录 scp免密传输
我们采用RSA非对称加密算法,原理: 如果,A要和B通讯,则: (1). A通过RSA算法生成公钥(.pub)和私钥(公钥用于加密,私钥用于解密) (2). B将A的公钥文件(.pub)内容加入到au ...
- [CentOS7] parted用于磁盘分区(同时支持GPT和MBR分区表)
声明:本文主要总结自:鸟哥的Linux私房菜-第七章.Linux 磁碟與檔案系統管理,如有侵权,请通知博主 fdisk支持MBR分区表,gdisk支持GPT分区表,而parted支持两者 不知道为什么 ...
- python之03编码学习
编码介绍 ASCII :只能存英文和拉丁字符,一个字符占一个字节,8位 在中国的发展: gb2312:存6700多个中文 1980年 gbk1.0 :存2万多字符 1 ...
- UML——初识
初识 刚刚接触到UML的时候,先看的书,对整本书的内容做了宏观的把控.感觉UML这个东西和自己想象中的不一样.起初我认为它只是一个工具,将软件开发过程中不同的阶段用不用种类的图表现出来,后来才发现它是 ...
- bzoj3681: Arietta(网络流)
传送门 主席树优化建图? 然而我连代码都看不懂 贴个题解好了->这里 //minamoto #include<iostream> #include<cstdio> #in ...
- jquery json实现省市级级联
java后台程序: import java.util.HashMap;import java.util.List;import java.util.Map; import javax.servlet. ...
- Batch the files in the directory
#!/bin/bash #sourceFolder = /home/bigdatagfts/pl62716/refdata #targetFolder = /home/bigdatagfts/pl62 ...
- LeetCode 024 Swap Nodes in Pairs 交换链表中相邻的两个节点
Given a linked list, swap every two adjacent nodes and return its head.For example,Given 1->2-> ...
- Android启动时闪一下黑屏或者白屏
1.设定主题,此主题为透明的,加入到res/values/styles.xml中: <style name="Theme.AppStartLoadTranslucent" p ...
- Vue provide/inject 部分源码分析 实现响应式数据更新
provide/inject 数据响应式更新的坑及源码解析 下面是我自己曾经遇到 一个问题,直接以自己QA的形式来写吧 自问自答了,需要的同学也可以直接访问segmentfault地址 官网给出实例, ...