poj2104 划分树 区间K大 在线 无修改
博主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 ...
随机推荐
- ASP.Net初级学习一(基本语句入门)
<body > <form method="post" action="program.ashx"> <input type=&q ...
- HDU 2639 背包第k优解
Bone Collector II Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- javascript功能封装
实现方式其实很简单,我在代码打上注释,大家就懂了! var _date=[],dateData=["1月","2月","3月",&qu ...
- 面包旅行Android业务设计分析
面包旅行的业务设计不错,Android app也是清晰简洁又大方的样子,所以画了个业务脑图出来. 重要的几个业务特点分析如下: 1.账号绑定社交账号,方便社交推广 2.城市猎人活动,通过内容.时间.地 ...
- 更改本地hosts文件
在 C:\Windows\System32\drivers\etc 下更改 hosts 文件 127.0.0.1 www.baidu.com 这样访问 www.baidu.com 这个地址,其实是访问 ...
- UVA 1638 Pole Arrangement
https://vjudge.net/problem/UVA-1638 题意: n根长度分别为1,2,3,4……n的木棍 将这些木棍竖着排成一列 问从左边看能看到L根,从右边看能看到R根的方案数 将木 ...
- 转【jenkins插件】
开源版本的Jenkins 具有三大能力:Master-Slave的分布式构建调度能力.Pipeline编排能力.强大的开源生态(插件)能力. 2017年4月,Jenkins创始人KK(Kohsuke ...
- mysql 可视化界面操作指令
1.让自增长从新开始 ALTER TABLE users auto_increment =1;//让表中的自增长从新从0开始 2.条件查询 SELECT name from users WHERE ...
- photoshop的魔棒工具怎么用来抠图
魔棒工具是photoshop中提供的一种可以快速形成选区的工具,对于颜色边界分界明显的图片,能够一键形成选区,方便快捷. 本教程通过一个简单的实例,教新手怎么用Photoshop魔棒工具快速形成抠图选 ...
- MySQL 5.7 跟踪优化器
Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 5Server version ...