【填坑】可持久化线段树解决无修改的区间k大问题
来源:互联网 发布:手机听小说软件 编辑:程序博客网 时间:2024/05/16 23:49
区间k大问题是一个比较经典的问题,各种方法层出不穷,写暴力的、树套树的、主席树的、分块大法好(@LOI_DQS)的……
这里讲一下权值线段树+可持久化解决的方法(不支持修改操作)
首先是权值线段树,顾名思义,就是按权值大小保存而不是按照序列顺序保存的线段树,如1,4,2,5,3,在权值线段树里保存的顺序应该为1,2,3,4,5
可持久化数据结构,就是指可以“持久”的数据结构,具体就是假设你现在做到了第100步,却突然想查询第50步时的状态(听起来很坑对不对)
可持久化如何实现呢?想具体了解的可以看wc2012 FHQ的论文《谈谈各种数据结构》,大概比较靠后的位置;不想具体了解的可以看我讲的……
Q:可持久化最简单的实现方法是什么?
A:[手动斜眼]每次操作后的数据结构存一遍,操作N次就存N棵树
Q:说的吼!但是空间太大了怎么办?
A:……
Q:一个数据结构修改要动多少东西?就拿线段树说吧
A:修改一次要改logN个节点的值
Q:那么剩下那些不用改的节点还用存吗?
A:……似乎不用了
Q:那么修改一次就等于把所有修改的节点另建立新节点,对吗?
A:我好像明白了些什么…………
黄色节点为要修改的节点,红色节点为新建的节点,黑色圈里圈着的是旧树,红色的圈里圈着的是新树,可以看到,修改后的新树只是在旧树的基础上修改了几个节点,所以只需要新建这几个节点,并把新树的部分指向指回旧树
这个就是可持久化的实现方式了,那么,可持久化和权值线段树怎么解决区间k大问题呢?
首先把值全部排序去重,用于建权值线段树,可以用stl中的sort和unique解决……权值线段树里保存的内容是值的数量,比如插入三个3就记录为3,再然后按照序列顺序依次插入节点,由于这是可持久化线段树,所以请用可持久化的方法插入……
什么,你说线段树不支持插入?你开始建棵空树啊,反正你都知道权值了,照着往下找,把插入改成单点+1不久完了吗?
然后就到了查询了,首先因为你是可持久化的,那么查询l到r区间就是你第r次插入减去第l-1次插入后的线段树的样子,想想对不对,可持久化后你总共建立了n棵线段树,两棵线段树相减,得到的就是一棵只有中间过程的线段树,在这里也就是第l次操作到第r次操作了
那么我们得到了这个区间,就可以继续找第k大了,怎么找?因为这是权值线段树,所以是排好序的~,那么就可以像平衡树一样找啦
代码么……
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;int ls[5000000]; //左儿子int rs[5000000]; //右儿子int num[5000000]; //过会再讲int san[5000000]; //过会再讲int sum[5000000]; //线段树里保存的值int T[5000000]; //每个节点在线段树里的标号int n,m,tot = 0;
建树函数:
void build(int l,int r,int &x){ x = ++tot;//不再使用堆式存储,而是动态开点 sum[x] = 0;//初始是一棵空树 if(l == r) return ; int mid = (l+r)/2; build(l,mid,ls[x]); build(mid+1,r,rs[x]);}
修改操作:
void update(int last,int p,int l,int r,int &x)//p点加1{ x = ++tot; ls[x] = ls[last]; rs[x] = rs[last]; sum[x] = sum[last] + 1;//首先继承之前的线段树 if(l == r) return ; int mid = (l+r)/2; if(p <= mid) update(ls[last],p,l,mid,ls[x]); else update(rs[last],p,mid+1,r,rs[x]);//分清情况,只往一边建}
查询操作:
int query(int s,int t,int l,int r,int k)//查询s到t区间第k大的数{ if(l == r) return l; int mid = (l+r)/2; int cnt = sum[ls[t]] - sum[ls[s]];//cnt为左子树新树减旧树 if(k <= cnt) return query(ls[s],ls[t],l,mid,k); return query(rs[s],rs[t],mid+1,r,k-cnt);}
主函数及预处理:
int main(){ int x,y,z; scanf("%d%d",&n,&m); for(int i = 1;i <= n;i ++) { scanf("%d",&san[i]); num[i] = san[i]; } sort(san+1,san+n+1); int cnt = unique(san+1,san+n+1)-san-1; build(1,cnt,T[0]); for(int i = 1;i <= n;i ++) num[i] = lower_bound(san+1,san+cnt+1,num[i]) - san; for(int i = 1;i <= n;i ++) update(T[i-1],num[i],1,cnt,T[i]); for(int i = 1;i <= m;i ++) { scanf("%d%d%d",&x,&y,&z); printf("%d\n",san[query(T[x-1],T[y],1,cnt,z)]); } return 0;}
代码不长,比较简单,也就懒得解释了……
- 【填坑】可持久化线段树解决无修改的区间k大问题
- 区间第K值(可持久化线段树)
- 区间第k小 poj2104 可持久化线段树
- 可持久化线段树|主席树 POJ 2104 区间第k大的数
- 可持久化线段树入门题 hdu2665 求区间第k小的数
- 关于实时开节点的可持久化线段树及区间/树链第K大值
- POJ2104-K-th Number-区间第k大-可持久化线段树/主席树
- POJ 2104 K-th Number 区间第K大,可持久化线段树
- 【HDU4348】To The Moon-主席树(可持久化线段树)区间修改+区间询问
- 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】
- poj 2104 hdu 2665 区间第k大 可持久化线段树
- bzoj 1901 有更新区间第k大 树状数组套可持久化线段树
- POJ 2104 可持久化线段树 求区间第k大
- 【可持久化线段树】poj 2104 静态区间第k大
- 可持久化线段树——Step 1 静态区间第K大
- poj 2104 可持久化线段树 区间K大 不修改
- hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)
- hdu 4348 可持久化线段树(区间和
- Asp.Net MVC3 简单入门详解过滤器Filter
- 字符串比较 : "11"和"4" 谁大呢?
- meta name="renderer" 控制默认浏览器内核
- jmeter(二十三)-关联设置
- MySQL 如何在线备份历史表
- 【填坑】可持久化线段树解决无修改的区间k大问题
- 对Android中的堆栈的理解(Stack<E>)
- Mybatis3源码分析(01)-前期准备
- 将 Ceph 存储集群集成到 OpenStack 云中
- 日期函数应用
- 笔记1.0.0
- JMeter学习(二十五)HTTP属性管理器
- jdbc连接数据库的步骤
- windows环境下nodejs安装