POJ 2104为例(主席树入门题)

思想:

可持久化线段树,也叫作函数式线段树,也叫主席树(高大上)。

可持久化数据结构(Persistent data structure):利用函数式编程的思想使其支持询问历史版本、同时充分利用它们之间的共同数据来减少时间和空间消耗。

主席树:对原序列的每一个前缀[1..i]建立出一棵线段树维护值域上每个数的出现次数(所以要先离散化)。线段树每个节点保存的是区间中前缀对应的出现的次数

注意:

  • 这里没有使用指针,而是给每个节点编号,通过编号来将节点与左右子节点连接起来。
  • 对于前缀[1,i]和前缀[1,i+1]的线段树,如果离散化后newa[i+1]<=mid ,那么这两棵线段树的右边是完全相同的,不需要重复建立。
  • 查询过程,先查看左子树中元素的出现次数是否大于k,如果是,继续查左子树,反之查询右子树。
  • 同一区间出现次数可以直接相减得到。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;//[]
const int maxn = 100010, maxm = 20 * maxn;
int tot, c;
int a[maxn], newa[maxn];
int lson[maxm], rson[maxm], t[maxm], tree[maxm];
//lson,rson记录左右节点标号,t记录每一个前缀构成的线段树的根节点标号,tree记录标号对应区间中数字出现次数
int compress(int x)//离散化
{
return lower_bound(newa+1, newa+1+c, x) - newa;
}
int build(int l, int r)
{
int root = tot++; tree[root] = 0;
int mid = (l+r)/2;
if(l == r ) return root;
lson[root] = build(l, mid);
rson[root] = build(mid + 1, r);
return root;
}
void update(int root, int newroot, int l, int r, int num)
{
tree[newroot] = tree[root] + 1;
if(l == r) return;
int mid = (l + r)/2;
if(num <= mid){
lson[newroot] = tot++;//有变动,重新建立
rson[newroot] = rson[root];//右边不变
update( lson[root], lson[newroot], l, mid, num);
}else{
rson[newroot] = tot++;//有变动,重新建立
lson[newroot] = lson[root];//左边不变
update(rson[root], rson[newroot], mid + 1, r, num);
}
}
int query(int leftroot, int rightroot, int l, int r, int k)
{
if(l == r ) return l;
int mid = (l + r)/2;
if(tree[lson[rightroot]] - tree[lson[leftroot]] >= k){
query(lson[leftroot], lson[rightroot], l, mid, k);
}else{
int temp = tree[lson[rightroot]] - tree[lson[leftroot]];
query(rson[leftroot], rson[rightroot], mid + 1, r, k - temp);
}
}
int main (void)
{
int n,m;scanf("%d%d",&n,&m);
tot = 0;
for(int i = 1; i <= n; i++) {
scanf("%d",&a[i]);
newa[i] = a[i];
}
sort(newa+1, newa+1+n);
c = unique(newa+1, newa+1+n) - newa-1;//去重
t[0] = build(1, c);//初始化
for(int i = 1; i <= n; i++){
t[i] = tot++;
update(t[i-1], t[i], 1 , c, compress(a[i]));//不断更新,建树
}
int l, r, k;
while(m--){
scanf("%d%d%d",&l,&r,&k);
printf("%d\n", newa[query(t[l-1], t[r], 1 ,c, k)]);
}
return 0;
}//1800ms

还是划分树快些。。。

真的是理解花了好久,连写再调试又花了好久。。。。。。然而只学了点毛皮。

动态区间第k大貌似要用到树状数组,过几天再来研究一下!

最新文章

  1. 【译】Import Changes from Direct3D 11 to Direct3D 12
  2. Ajax作用、及Ajax函数的编写
  3. opencv8-GPU之相似性计算
  4. springMVC简单示例
  5. ADF_Starting系列8_使用EJB/JPA/JSF通过ADF构建Web应用程序之扩展UI Method
  6. 设计模式 策略-Strategy,装饰-Decorator,观察者-Observer
  7. jsp request.getParameterValues获取数组值代码示例
  8. FTP服务器中文环境引起润日下载不了附件问题解析
  9. 关于智普 - 千人免费学|Python培训|国内最权威python培训|html5
  10. 201621123031 《Java程序设计》第1周学习总结
  11. asp.net core系列 61 Ocelot 构建服务发现简单示例
  12. Invoker-n颜色涂m个珠子的项链
  13. NLP是什么
  14. [转] js在浏览器端对二进制流进行AES加密和解密
  15. How to leave the open file in eclipse tab after search?
  16. spring mvc发送请求404,不能进入处理方法,也不报错
  17. RocketMQ 消息发送
  18. android--------自定义Dialog之信息提示
  19. ionic 编写自定义控件
  20. c# AOP编程:Context与方法拦截

热门文章

  1. IIS 的最大并发数
  2. SQLServer性能优化专题
  3. org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [********] will not be managed by Spring
  4. WebService 服务开发
  5. 2.10.1 article元素
  6. 【C语言】控制台窗口图形界面编程(四):文本输出
  7. 【MSSQL】MDF、NDF、LDF文件的含义
  8. thinkphp5生成二维码
  9. oracle学习链接
  10. oracle调用存储过程和函数返回结果集