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 ...
随机推荐
- Hadoop及Zookeeper+HBase完全分布式集群部署
Hadoop及HBase集群部署 一. 集群环境 系统版本 虚拟机:内存 16G CPU 双核心 系统: CentOS-7 64位 系统下载地址: http://124.202.164.6/files ...
- Mybatis手工写sql语句及Mapper.xml方法
首先在项目中 建一个mapper包,然后在spring集合mybatis的配置文件中设置扫描这个mapper包 然后,建 封装查询结果需要的 pojo 然后,在 mapper包中创建 Mapper接口 ...
- 题解 【luogu P2680 NOIp提高组2015 运输计划】
题目链接 题解 题意 一棵树上有\(m\)条路径,可以将其中一条边的权值改为0,问最长的路径最短是多少 分析 最短的路径最长自然想到二分最长路径,设其为\(dis\) 关键在于如何check chec ...
- POJ 2976 二分
Dropping tests Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12515 Accepted: 4387 D ...
- iostat和iowait详细解说
简单的说,sar -u看出来的cpu利用率iowait 不实用,iostat -x 中的 svctm 和util 参数 命令形式: iostat -x 1 每隔一秒输出下 其中的svctm参数代表 ...
- git revert HEAD
使用git reset回退公共远程分支的版本后,需要其他所有人手动用远程master分支覆盖本地master分支,显然,这不是优雅的回退方法,下面我们使用另个一个命令来回退版本: git revert ...
- EasyUI Tree递归方式获取JSON
最近需要用到EASYUI中的TREE功能,以前我是直接拼接成<UL><LI>发现这样拼完之后在更改树后对树的刷新不是很理想,现改用JSON格式,首先分析TREE中JOSN格式如 ...
- linux下输出查看进程及杀进程
1.查找有关tomcat的进程 ps -ef | grep tomcat 2.查看某端口占用情况 netstat -tulpn | grep 9009 3.杀进程 普通:kill 进程id 强制:ki ...
- UVA 1393 Highways
https://vjudge.net/problem/UVA-1393 题意: a*b的点阵中能画多少条非水平非竖直的直线 方向‘/’ 和 方向 ‘\’ 对称 枚举直线所在矩形的i*j 直线可能重复的 ...
- 汕头市队赛SRM 20 T2不净的圣杯
不净的圣杯 SRM 20 背景 作为一张BUG级别的卡,官方打算把它修改得人畜无害一些…… 虽然名字还没想好,但是能力大概是对敌方所有单位造成d点伤害,d为自己牌组中所有卡的编号的最大公约数.这无疑是 ...