题目大意:

求序列的区间第k大

基本思路:

因为我根本就没有思路,知道这是主席树,我就去学了下,在b站上看了uestc的教学视频,然后看了一篇博客,博客http://www.cnblogs.com/Empress/p/4652449.html,我觉得理解的无修改主席树差不多了,也没那么难。下面代码是按照b站上来的,我很喜欢他的离散化方式,不过我不喜欢他的代码风格。

下面阐述我所理解的主席树的基本思路和细节:

转自:http://www.cnblogs.com/Empress/p/4652449.html

这种求区间第k(大)小的题目

最容易想到的做法就是对于每个询问,对[l, r]区间排个序,输出第k小,这样的复杂度是O(m×nlognm×nlogn)

大家都很容易想到排序,但是对于每个询问每个区间排序的代价太大了...

再想想,让我们加入一些线段树的思想,

要求第k小,也就是与个数相关,那么我们可以 以[l,r]区间内的数的个数来建立一棵线段树

结点的值是数的个数,当我们要找第k小的数时,若左子树大于k,那么很显然第k小的数在左子树中;若左子树小于k,那么第k小的数在右子树中

建树的复杂度是O(nlogN),查询的复杂度是O(logN)      (这里的N是不相同数的数量)

若我们仍对每个查询建树,那么复杂度丝毫没有降低(反而提高了),那有没有什么办法可以不要每次查询都建树呢?

(让我们联想一下前缀和) 假设我们知道[1, l-1]之间有多少个数比第k小的数小,那么我们只要减去这些数之后在[1, r]区间内第k小的数即是[l, r]区间内的第k小数

更确切的说,我们要求[l, r]区间内的第k小数  可以 用以[1, r]建立的线段树去减去以[1, l-1] 建立的线段树

这样能够减的条件是这两棵树必须是同构的。

若是不太明白, 我们来举个例子:

如有序列  1 2 5 1 3 2 2 5 1 2

我们要求 [5,10]第5小的数

(数列中不存在4、6、7、8 但根据原理就都写出来了,为方便理解,去掉了hash的步骤,实际的代码中其实只要一棵4个叶子节点的树即可)

(红色的为个数)

我们建立的[1, l-1] (也就是[1, 4])之间的树为

[1, r]也就是[1, 10]的树为

两树相减得到

我们来找第5小的数:

发现左子树为5  所以第5小的数在左边, 再往下(左4右1) 发现左边小于5了 ,所以第5小的数在右边 所以第5小的数就是3了

同样的,我们只要建立[1, i] (i是1到n之间的所有值)的所有树,每当询问[l, r]的时候,只要用[1, r]的树减去[1, l-1]的树,再找第k小就好啦

我们将这n个树看成是建立在一个大的线段树里的,也就是这个线段树的每个节点都是一个线段树( ——这就是主席树)

最初所有的树都是空树,我们并不需要建立n个空树,只要建立一个空树,也就是不必每个节点都建立一个空树

插入元素时,我们不去修改任何的结点,而是返回一个新的树( ——这就是函数式线段树)

因为每个节点都不会被修改,所以可以不断的重复用,因此插入操作的复杂度为O(logn)

总的复杂度为O((n+m)lognlogN)   (听说 主席树的芭比说 加上垃圾回收, 可以减少一个log~~~ 然而这只是听说)

你以为这样就结束了吗!!

你没有发现这样空间大到爆炸吗!!!

你在每个节点都建了一个线!段!树!这不MLE才有鬼呢!!!

那怎么办呢?

TiTi表示一棵[1, i]区间的线段树

那么TiTi与Ti−1Ti−1的区别就只有当前插入的这个元素aiai以及它的父亲以及他父亲的父亲以及他父亲的父亲的父亲...

也就是改变的就只有他和他上面logn个数

所以,我们并不需要建一整棵树,我们只需要 单独建立logn个结点,跟Ti−1Ti−1连起来就好了

这样树的空间复杂度(NlogN)

个人认为关键是理解主席树保留了各个历史版本的线段树。

代码如下:

#include<vector>
#include<stack>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring> using namespace std; typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn =100000+10;
int n,m,cnt,root[maxn],a[maxn];
struct node{
int l,r,sum;
}T[maxn*40];
vector<int>vec;
int getid(int x){
return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
}
void update(int l,int r,int &x,int y,int pos){
//之所要用引用,是为了下面改变l,r等
T[++cnt]=T[y];
T[cnt].sum++;
x=cnt;
if(l==r){
return;
}
int mid=(l+r)/2;
if(mid>=pos){
update(l,mid,T[x].l,T[y].l,pos);
}else{
update(mid+1,r,T[x].r,T[y].r,pos);
}
}
int query(int l,int r,int x,int y,int k){
if(l==r){
return l;
}
int mid=(l+r)/2;
int sum=T[T[y].l].sum-T[T[x].l].sum;
//之所以是.l,见上面的例子
if(sum>=k){
return query(l,mid,T[x].l,T[y].l,k);
}else{
return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
vec.push_back(a[i]);
}
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
for(int i=1;i<=n;i++){
update(1,n,root[i],root[i-1],getid(a[i]));
}
for(int i=1;i<=m;i++){
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",vec[query(1,n,root[x-1],root[y],k)-1]);
//所以上面是访问了对应的历史版本的线段树
//因为之前离散化所以现在要还原回来 }
return 0;
}

  

  

poj 2104 无修改主席树的更多相关文章

  1. poj 2104 K-th Number 主席树+超级详细解释

    poj 2104 K-th Number 主席树+超级详细解释 传送门:K-th Number 题目大意:给出一段数列,让你求[L,R]区间内第几大的数字! 在这里先介绍一下主席树! 如果想了解什么是 ...

  2. SPOJ MKTHNUM & POJ 2104 - K-th Number - [主席树模板题]

    题目链接:http://poj.org/problem?id=2104 Description You are working for Macrohard company in data struct ...

  3. POJ 2104 K-th Number 主席树(区间第k大)

    题目链接: http://poj.org/problem?id=2104 K-th Number Time Limit: 20000MSMemory Limit: 65536K 问题描述 You ar ...

  4. poj 2104 K-th Number(主席树 视频)

    K-th Number 题意: 给你一些数,让你求一个区间内,第k大的数是多少. 题解: 主席树第一题,看的qsc视频写的,戳戳戳 学到了unique函数,他的作用是:把相邻的重复的放到后面,返回值是 ...

  5. POJ 2104 HDU 2665 主席树 解决区间第K大

    两道题都是区间第K大询问,数据规模基本相同. 解决这种问题, 可以采用平方划分(块状表)复杂度也可以接受,但是实际表现比主席树差得多. 这里大致讲一下我对主席树的理解. 首先,如果对于某个区间[L,R ...

  6. Poj 2104 K-th Number(主席树&&整体二分)

    K-th Number Time Limit: 20000MS Memory Limit: 65536K Case Time Limit: 2000MS Description You are wor ...

  7. POJ 2104 K-th Number 主席树

    #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> us ...

  8. [BZOJ3295] [Cqoi2011]动态逆序对(带修改主席树)

    题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序 ...

  9. 【bzoj1901】dynamic ranking(带修改主席树/树套树)

    题面地址(权限题) 不用权限题的地址 首先说说怎么搞带修改主席树? 回忆一般的kth问题,我们的主席树求的是前缀和,这样我们在目标区间的左右端点的主席树差分下就能求出kth. 那么我们如何支持修改操作 ...

随机推荐

  1. [BZOJ1023][SHOI2008]cactus仙人掌图 DP

    题目链接 套路就是先考虑一般的树上做法.求直径的dp的做法大家应该都会吧. 那么设\(dp[i]\)表示\(i\)的子树中的点到\(i\)的最大距离. 在dp的过程中 \[ ans=\max\{dp[ ...

  2. 20_1.Condition

    import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public clas ...

  3. Adblock Plus 添加过滤规则

    过滤掉相关的DIV 如要过滤某网站的 如例1:  home.firefoxchina.cn##div#module-game##元素#名字 过滤掉ID为名字的元素##div.名字 class为名字的D ...

  4. JS中的作用域及闭包

    1.JS中的作用域 在 es6 出现之前JS中只有全局作用域和函数作用域,没有块级作用域,即 JS 在函数体内有自己的作用域,但是如果不是在函数体的话就全部都是全局作用域.比如在 if.for 等有 ...

  5. Oracle or Question Solve(二)

    数据库常用语句和函数 ----update update()函数主要注意的是后面的where限制条件--例子:update tab_a a set a.v1 = (select b.v1 from t ...

  6. SoftDevice Specification v1.2

    S110 SoftDevice是蓝牙®低功耗(BLE)外设协议栈的解决方案.它集成了一个低 能量控制器和主机,并为建设蓝牙低功耗系统全面且灵活的API 芯片(SoC)解决方案. 本文件包含SoftDe ...

  7. pycharm运行html文件报404错误

    某次发现运行导入的html文件的时候,打开浏览器会报404错误: 而运行自己写的html文件则正常显示: 最后发现这是pycharm缓存问题,只需重启pycharm清除缓存就ok啦!

  8. 配置github的pull request触发jenkins自动构建

    参照: https://www.cnblogs.com/zanjiahaoge666/p/6402738.html 之前的配置,都是向master分支push操作触发jenkins进行构建,但是在一般 ...

  9. Asp.Net Core 第04局:依赖注入

    总目录 前言 本文介绍Asp.Net Core中默认的依赖注入(DI)模式. 环境 1.Visual Studio 2017 2.Asp.Net Core 2.2 开局 第一手:依赖注入说明 1.一个 ...

  10. MySQL数据库参数优化

    概述 最近在对各个系统的mysql做一些参数上的优化,也开了慢查询,准备后面针对特定sql再进一步优化.下面主要介绍一下一些优化的参数. 1.优化前mysql配置 可以看到基本上是没怎么做优化的. 2 ...