博主sbit。。。。对于高级数据结构深感无力,然后这些东西在OI竟然烂大街了,不搞就整个人都不好了呢。
于是我勇猛的跳进了这个大坑
——sbit
区间K大的裸题,在线,无修改。
可以用归并树(\(O(nlog^3n)\)),也可用划分树(\(O(nlogn + mlogn)\))。果断划分树。。。(以后再来看归并树。。。再来看。。。来看。。看。。)
划分树是个什么东西呢?为什么可以做区间k大呢?
想想平衡树做k大时是如何搞的,其实内在原理是一样的。
划分树分两个步骤:建树与询问。
1. 建树
划分树借鉴了快排的思想。划分树的每个节点保存了一个区间,以此区间为根节点,把区间分为左子树[left, mid]和右子树[mid + 1, right]的两个子树,保证左子树内的的值不大于根节点中中位数的值,右子树不小于之,且数值在子树中的顺序遵从在根节点中时的相对位置关系。关键之处在于,给每个数值记录一个to_left,表示从[left, i]中,被划分到左子树的值的数量,在查询中,这将起到至关的作用。对于没有相同取中位数值的元素时,只要比对大小关系来进行划分即可,但是,如果有相同取中位数值的元素时,如何处理这些元素呢?
method 1: 离散化。。简洁易懂,方便快捷。
method 2: 这个方法很巧妙,网上大多数代码(都是抄hh的,sbit也是的,羞耻play了)都使用了这个方法。参考资料1给出了详细的解释。
引用自参考资料1:
划分的时候还有一点需要处理:如果有多个数据相同怎么办呢?通过一种特殊的处理:尽量使左右两边平均分配相同的数。这个特殊处理是这样的:
在没分之前,先假设中位数左边的数据suppose都已经分到左边了,所以suppose=mid-left+1;然后如果真的分在左边,即if(tree[level][i]<sorted[mid])
suppose--;suppose就减一!到最后,如果suppos=1,则说明中位数左边的数都小于中位数,如果有等于中位数的,则suppose大于1。
最后分配的时候,把suppose个数,分到左边就可以了,剩下的分到右边!因为suppose的初值是mid-left+1,这样就能保证中位数左边和右边的数平衡了!
2. 询问
类似于平衡树求k大,利用上文求出来的to_left值,我们可以通过深入划分树的层级对k的值进行缩小,最后当区间长度等于1时,k等于1,答案只有一个——就是当前值啦!用纸画画就能明白了。
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = + ;
int val[][maxn], sorted[maxn], to_left[][maxn];
int n, m;
void build_tree(int l, int r, int layer) {
if(l == r) return ;
int mid = (l + r) >> ;
int suppose = mid - l + ;
for(int i = l; i <= r; ++i)
if(val[layer][i] < sorted[mid])
--suppose;
int rec_l = l, rec_r = mid + ;
for(int i = l; i <= r; ++i) {
if(i == l) {
to_left[layer][i] = ;
} else {
to_left[layer][i] = to_left[layer][i - ];
}
if(val[layer][i] < sorted[mid]) {
++to_left[layer][i];
val[layer + ][rec_l++] = val[layer][i];
} else if(val[layer][i] > sorted[mid]) {
val[layer + ][rec_r++] = val[layer][i];
} else {
if(suppose != ) {
--suppose;
++to_left[layer][i];
val[layer + ][rec_l++] = val[layer][i];
} else {
val[layer + ][rec_r++] = val[layer][i];
}
}
}
build_tree(l, mid, layer + );
build_tree(mid + , r, layer + );
} int query(int l, int r, int layer, int ql, int qr, int kth) {
if(l == r) return val[layer][l];
int s, ss;
if(l == ql) {
s = ;
ss = to_left[layer][qr];
} else {
s = to_left[layer][ql - ];
ss = to_left[layer][qr] - s;
}
int mid = (l + r) >> ;
if(kth <= ss) {
return query(l, mid, layer + , l + s, l + s + ss - , kth);
}
return query(mid + , r, layer + , mid + + ql - s - l, mid + + qr - l - s - ss, kth - ss);
} int main() {
#ifndef ONLINE_JUDGE
freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);
#endif
scanf("%d%d", &n, &m);
for(int i = ; i <= n; ++i) {
scanf("%d", &sorted[i]);
val[][i] = sorted[i];
}
sort(sorted + , sorted + n + );
build_tree(, n, );
while(m--) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", query(, n, , l, r, k));
}
return ;
}
参考资料:
- http://sbp810050504.blog.51cto.com/2799422/1008930
- http://blog.sina.com.cn/s/blog_5f5353cc0100ki2e.html
- http://www.cppblog.com/MatoNo1/archive/2011/06/27/149604.html
- http://www.xuebuyuan.com/829409.html
- http://shizhixinghuo.diandian.com/post/2012-09-02/40037691896
- http://baike.baidu.com/view/4199603.htm
- http://barty.ws/partitiontree-%E5%88%92%E5%88%86%E6%A0%91/
poj2104 划分树 区间K大 在线 无修改的更多相关文章
-
poj2104 主席树 区间K大 在线 无修改
关于主席树: 主席树(Chairman Tree)是一种离线数据结构,使用函数式线段树维护每一时刻离散之后的数字出现的次数,由于各历史版本的线段树结构一致,可以相减得出区间信息,即该区间内出现的数字和 ...
-
poj2761Feed the dogs(划分树-区间K值)
链接 这树着实不好理解啊 讲解http://www.cnblogs.com/pony1993/archive/2012/07/17/2594544.html 对于找K值 右区间的确定不是太理解..先当 ...
-
POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)
题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几 分析 : 求取区间 K 大值是个经典的问题,可以使用 ...
-
HDU 4417 (划分树+区间小于k统计)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4417 题目大意:给定一个区间,以及一个k值,求该区间内小于等于k值的数的个数.注意区间是从0开始的 ...
-
动态求区间K大值(权值线段树)
我们知道我们可以通过主席树来维护静态区间第K大值.我们又知道主席树满足可加性,所以我们可以用树状数组来维护主席树,树状数组的每一个节点都可以开一颗主席树,然后一起做. 我们注意到树状数组的每一棵树都和 ...
-
Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)(值域线段树第k大)
Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序 ...
-
hdu2665 &;&; poj2104划分树
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 47066 Accepted: 15743 Ca ...
-
poj2104(划分树模板)
poj2104 题意 给出一个序列,每次查询一个区间,要求告诉这个区间排序后的第k个数. 分析 划分树模板,O(mlogn). 建树.根据排序之后的数组,对于一个区间,找到中点的数,将整个区间分为左右 ...
-
树上前k大的包含不重复结点的长链
一棵树,不一定是二叉树,在每个结点最多只属于一条链的情况下,处理出其中最长的前k个的长度. 最近训练赛做到两道题了,有必要总结一下. 不过我不知道是否有更专门的叫法. 借鉴了这位大佬的博客:https ...
随机推荐
-
C和指针 第十二章 使用结构和指针
链表是一种常用的数据结构,每个节点通过链或者指针链接在一起,程序通过间接指针访问链表中的节点. typedef struct Node { //指向下一个节点的指针 struct Node *next ...
-
计算excel列的名字
#include <iostream> using namespace std; int main() { unsigned int column; cin>> ...
-
解决CSDN的code功能,无法git clone多个项目的问题
几天前在使用CSDN的git功能的时候发现一个问题:我在CSDN上创建了两个项目,但是却只能git clone其中的一个. 原因: 在添加ssh公钥的时候,将主机上的ssh公钥在CSDN上填的地方不合 ...
-
inux平台的C与C++
课堂里学不到的C与C++那些事(一) 首先,声明一下这是一个系列的文章.至于整个系列有多少篇,笔者也不知道,不知道有多少篇,也不知道多久会更新一篇.反正只有一个原则,写出来的文 章能见得人才会公布出来 ...
-
网站开发进阶(三十八)Web前端开发规范文档你需要知道的事
Web前端开发规范文档你需要知道的事 规范目的 为提高团队协作效率, 便于后台人员添加功能及前端后期优化维护, 输出高质量的文档, 特制订此文档. 本规范文档一经确认, 前端开发人员必须按本文档规范进 ...
-
MySql 游标定义时使用临时表
参考:Re: Temp Table in Select of a Cursor 方法一: delimiter $$ create procedure test_temp() begin drop te ...
-
详解 JVM Garbage First(G1) 垃圾收集器(转载)
前言 Garbage First(G1)是垃圾收集领域的最新成果,同时也是HotSpot在JVM上力推的垃圾收集器,并赋予取代CMS的使命.如果使用Java 8/9,那么有很大可能希望对G1收集器进行 ...
-
openstack环境搭建常用命令
1,编辑/etc/selinux/config文件,关闭selinux SELINUX=disabled 2,清空iptables规则并保存 # iptables -F # service iptab ...
-
ISO 7816-4: Interindustry Commands for Interchange
5. Basic Organizations 5.1 Data structures5.2 Security architecture of the card 5.3 APDU message str ...
-
mongo学习- group操作
group可以使用 $sum,$avg,$max,$min,$first,$last