博主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 ;
}

参考资料:

  1. http://sbp810050504.blog.51cto.com/2799422/1008930
  2. http://blog.sina.com.cn/s/blog_5f5353cc0100ki2e.html
  3. http://www.cppblog.com/MatoNo1/archive/2011/06/27/149604.html
  4. http://www.xuebuyuan.com/829409.html
  5. http://shizhixinghuo.diandian.com/post/2012-09-02/40037691896
  6. http://baike.baidu.com/view/4199603.htm
  7. http://barty.ws/partitiontree-%E5%88%92%E5%88%86%E6%A0%91/

poj2104 划分树 区间K大 在线 无修改的更多相关文章

  1. poj2104 主席树 区间K大 在线 无修改

    关于主席树: 主席树(Chairman Tree)是一种离线数据结构,使用函数式线段树维护每一时刻离散之后的数字出现的次数,由于各历史版本的线段树结构一致,可以相减得出区间信息,即该区间内出现的数字和 ...

  2. poj2761Feed the dogs(划分树-区间K值)

    链接 这树着实不好理解啊 讲解http://www.cnblogs.com/pony1993/archive/2012/07/17/2594544.html 对于找K值 右区间的确定不是太理解..先当 ...

  3. POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)

    题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几 分析 : 求取区间 K 大值是个经典的问题,可以使用 ...

  4. HDU 4417 (划分树+区间小于k统计)

    题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=4417 题目大意:给定一个区间,以及一个k值,求该区间内小于等于k值的数的个数.注意区间是从0开始的 ...

  5. 动态求区间K大值(权值线段树)

    我们知道我们可以通过主席树来维护静态区间第K大值.我们又知道主席树满足可加性,所以我们可以用树状数组来维护主席树,树状数组的每一个节点都可以开一颗主席树,然后一起做. 我们注意到树状数组的每一棵树都和 ...

  6. Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)(值域线段树第k大)

    Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序 ...

  7. hdu2665 && poj2104划分树

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 47066   Accepted: 15743 Ca ...

  8. poj2104(划分树模板)

    poj2104 题意 给出一个序列,每次查询一个区间,要求告诉这个区间排序后的第k个数. 分析 划分树模板,O(mlogn). 建树.根据排序之后的数组,对于一个区间,找到中点的数,将整个区间分为左右 ...

  9. 树上前k大的包含不重复结点的长链

    一棵树,不一定是二叉树,在每个结点最多只属于一条链的情况下,处理出其中最长的前k个的长度. 最近训练赛做到两道题了,有必要总结一下. 不过我不知道是否有更专门的叫法. 借鉴了这位大佬的博客:https ...

随机推荐

  1. [mysql]tpcc相关及画图

    参考:http://blog.chinaunix.net/uid-26896862-id-3563600.html 参考:http://blog.chinaunix.net/uid-25266990- ...

  2. 【题解】我也不是B ifrog 1112 二分 倍增

    题目传送门:http://ifrog.cc/acm/problem/1112 神奇的倍增二分,长见识了,在此做个记录,分享给大家. 懒得写题解了,直接转YJQ的:http://ifrog.cc/acm ...

  3. [Android问答] px、dp和sp,这些单位有什么区别?

    相信每个Android新手都会遇到这个问题,希望这篇帖子能让你不再纠结. px: 即像素,1px代表屏幕上一个物理的像素点: px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小 ...

  4. 百度地图定位API,精度提高

    我使用百度定位API DEMO上面好像就可以setCoorType("bd09ll");//百度地图坐标. 然后我找了下从其它坐标体系迁移到百度坐标. 问下: 1.那我还能不能在百 ...

  5. HDFS error

    错误信息描述: HDFS error: could only be replicated to 0 nodes, instead of 1;以及由此衍生出来的种种奇葩问题(具体的错误信息见后面),下面 ...

  6. 【51NOD-0】1019 逆序数

    [算法]离散化+树状数组(求逆序对) [题解]经典,原理是统计在i之前插入的且值≤i的个数,然后答案就是i-getsum(i) #include<cstdio> #include<a ...

  7. java分页通用篇

    一.创建分页通用类 package com.dkyw.util; import java.util.List; public class Page<T> { private int tot ...

  8. UIScrollView---iOS-Apple苹果官方文档翻译

      本系列所有文章,链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址(2013年12月29日更新版) //转载请注明出处--本文永久链接:http://www ...

  9. MapperScannerConfigurer不 property-placeholder

    关于org.mybatis.spring.mapper.MapperScannerConfigurer不支持 property-placeholder 参考了http://www.oschina.ne ...

  10. CPU架构及并发编程基础(一)

    一.intel cpu发展计划tick-tock Tick-Tock是Intel发展微处理器芯片设计制造业务的一种战略模式.Intel指出,每一次处理器微架构的更新和每一次芯片制程的更新遵循“Tick ...