【POJ】【2104】区间第K大
可持久化线段树
可持久化线段树是一种神奇的数据结构,它跟我们原来常用的线段树不同,它每次更新是不更改原来数据的,而是新开节点,维护它的历史版本,实现“可持久化”。(当然视情况也会有需要修改的时候)
可持久化线段树的应用有很多,仅以区间第K大这种简单的问题来介绍这种数据结构。
我们原本建立的线段树是表示区间的,或者说,维护的是【位置】,存的是每个位置上的各种信息。它的优点是满足区间加法,但不满足区间减法,所以我们这里要换一种建树方式:对于每个区间[1,i]建立一棵权值线段树。这个线段树的作用其实就跟前缀和差不多,且像前缀和一样满足区间减法!只不过我们在求前缀和的时候保留的是sum,而权值线段树把所有的值都存下来了。
这里说一下它的保存方式:对[1,x]这个节点,它需要维护一个cnt值,表示在[1,x]这个值域,有cnt个数。
举个栗子,我们现在有一个序列{1,2,3,4,5,2,3,3,3,3}
然后对于表示区间[1,10]的线段树,它的节点是这样建的
可以看出,值在[1,5]的有10个数,在[1,2]的有3个数……以此类推
那么我们在查询第K大的时候,就可以像平衡树那样!如果左儿子的cnt>=k则在左边找,否则在右边找,那么我们就可以顺利地查询到第K大了~
那么问题来了:如果我想查询[3,7]这个区间上第3大的数应该怎么办呢?(这个地方容易晕,一定要分清原序列的区间和值域,虽然都是用方括号的区间表示的……如果看了这句话更晕了,那就忘了它吧)
那么就要回到我们之前说的【前缀和】上来了,我们以前快速求[l,r]的区间和,是利用前缀和[1,l-1]和[1,r]区间相减快速计算的,同理,我们也可以利用[1,l-1]和[1,r]两棵线段树来进行区间第K大的查询。即在两棵树上同时往下走!详见代码。
//POJ 2104
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
using namespace std;
const int N=;
//#define debug struct node{
int x,num,rank;
}a[N];
bool cmpx(node a,node b){
return a.x<b.x;
}
bool cmpn(node a,node b){
return a.num<b.num;
} struct Tree{
int cnt,l,r;
}t[N*];
int root[N],cnt=,n,m; #define mid (l+r>>1)
void updata(int &o,int l,int r,int pos){
t[++cnt]=t[o], o=cnt, ++t[o].cnt;
if (l==r) return;
if (pos<=mid) updata(t[o].l,l,mid,pos);
else updata(t[o].r,mid+,r,pos);
#ifdef debug
printf("%d %d %d %d\n",o,l,r,pos);
#endif
} int query(int i,int j,int rank){
i=root[i],j=root[j];
int l=,r=n;
while(l!=r){
if (t[t[j].l].cnt-t[t[i].l].cnt>=rank)//在两棵树上一起往下走
r=mid,i=t[i].l,j=t[j].l;
else{
rank-=t[t[j].l].cnt-t[t[i].l].cnt;
l=mid+,i=t[i].r,j=t[j].r;
}
}
return l;
}
#undef mid int main(){
freopen("file.in","r",stdin);
scanf("%d%d",&n,&m);
int x=;
F(i,,n) {scanf("%d",&a[i].x); a[i].num=i;}
sort(a+,a+n+,cmpx);
F(i,,n) a[i].rank=i;
sort(a+,a+n+,cmpn);
F(i,,n) {
root[i]=root[i-];
updata(root[i],,n,a[i].rank);//此处可以先不理解……
//简单来说就是:为了节约空间,我们并不需要真的给每个区间建一棵完整的线段树
//而是可以在原来的基础上进行新的维护(即原来的为历史版本)
}
sort(a+,a+n+,cmpx);
F(i,,m){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",a[query(l-,r,k)].x);
}
return ;
}
【POJ】【2104】区间第K大的更多相关文章
- POJ 2104 区间第k大(主席树)
题目链接:http://poj.org/problem?id=2104 题目大意:给定还有n个数的序列,m个操作,每个操作含有l,r,k,求区间[l,r]第k大 解题思路:线段树只能维护序列的最大值最 ...
- Poj 2104区间第k大(归并树)
题目链接 K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 36890 Accepted: 11860 C ...
- POJ 2104 K-th Number 主席树(区间第k大)
题目链接: http://poj.org/problem?id=2104 K-th Number Time Limit: 20000MSMemory Limit: 65536K 问题描述 You ar ...
- POJ 2104 静态找区间第k大
静态区间第k大的问题,往往可以利用主席树来解决 这是主席树的第一道题 主席树大概可以理解为在n个节点上都建立一棵线段树,但是想想会超出内存 每一个节点保存的线段树都记录当前整段前缀区间的信息 但是因为 ...
- poj 2104 主席树(区间第k大)
K-th Number Time Limit: 20000MS Memory Limit: 65536K Total Submissions: 44940 Accepted: 14946 Ca ...
- POJ 2104 && POJ 2761 (静态区间第k大,主席树)
查询区间第K大,而且没有修改. 使用划分树是可以做的. 作为主席树的入门题,感觉太神奇了,Orz /* *********************************************** ...
- POJ 2104 HDU 2665 主席树 解决区间第K大
两道题都是区间第K大询问,数据规模基本相同. 解决这种问题, 可以采用平方划分(块状表)复杂度也可以接受,但是实际表现比主席树差得多. 这里大致讲一下我对主席树的理解. 首先,如果对于某个区间[L,R ...
- 解决区间第K大的问题的各种方法
例题:http://poj.org/problem?id=2104 最近可能是念念不忘,必有回响吧,总是看到区间第k大的问题,第一次看到是在知乎上有人面试被弄懵了后来又多次在比赛中看到.以前大概是知道 ...
- 静态区间第k大(主席树)
POJ 2104为例(主席树入门题) 思想: 可持久化线段树,也叫作函数式线段树,也叫主席树(高大上). 可持久化数据结构(Persistent data structure):利用函数式编程的思想使 ...
随机推荐
- html5面向对象做一个贪吃蛇小游戏
canvas加面向对象方式的贪吃蛇 2016-08-25 这个小游戏可以增加对面向对象的理解,可以加强js逻辑能力,总之认真自己敲一两遍收获还是不少啊!!适合刚学canvas的同学练习!! 废话不多说 ...
- laravel5.2 学习之服务提供者
契约接口:app\Contracts\LanguageContract.php <?php namespace App\Contracts; interface LanguageContract ...
- Ubuntu12.04卡死的解决方案
刚开始安装的时候用着还行,不过后来发现用了一会总是会出现卡死的状况 后来看了下ubuntu12.04的内核是3.2,后来把内核升级到3.5发现这种情况不会出现了. 查看内核以及升级内核 uname - ...
- PopupWindow的简单使用
测试代码: package com.zzw.testpopuwindows; import android.app.Activity; import android.graphics.Color; i ...
- [terry笔记]RMAN综合学习之配置
[terry笔记]RMAN综合学习之备份http://www.cnblogs.com/kkterry/p/3308405.html [terry笔记]RMAN综合学习之恢复 http://www.cn ...
- activiti
http://activiti.org/designer/archived/ eclipse plugin
- move和转发
总的来说C++09跟C++98相比的变化是极其重大的.这个变化体现在三个方面,一个是形式上的变化,即在编码形式层面的支持,也就是对应我们所谓的编程范式(paradigm).C++09不会引入新的编程范 ...
- ORA-14099 错误解决
DB: 11.2.0.3.0 在测试把普通表修改为交换分区的时候,出现ORA-14099: all rows in table do not qualify for specified partiti ...
- Linux编译内核提示'make menuconfig' requires the ncurses libraries错误
原来使用的ubuntu 11.10系统由于误操作,导致系统崩溃,重新安装了ubuntu 11.10: 在编译内核的时候,提示如下错误: dingq@wd-u1110:~/hwsvn/2sw/1prj_ ...
- Android--简单开发和使用ContentProvider数据共享
今天学习的时候学到了ContentProvider数据共享这个东东,所以自己写了个小例子: 我们要开发ContentProvider的话,需要创建一个类去继承ContentProvider,里面会让你 ...