主席树(Kth number,HDU 2665)
来源:互联网 发布:淘宝上的qq号能买吗 编辑:程序博客网 时间:2024/06/05 07:03
题目链接:https://vjudge.net/problem/HDU-2665
参考博客:http://blog.csdn.net/acdreamers/article/details/8656644
感觉网上的资料不是很好,讲得也模糊,模板也没有。
还是大致看一下原理,然后通过模板题及代码来学习比较有效。
原理的话大白书P397那一段介绍挺好的,后面的图也挺直观的。当然网上搜罗一下也挺不错。
模板题就这道了,代码找个清晰靠谱的参考一下就挺好。
个人理解
主席树就是一个可以保留历史版本的线段树,通过动态开点和连接的方式来减少空间开销。
可以保留历史版本直观上理解就是可以解决询问历史情况的问题。
扩展的理解和使用就是可以维护一系列的线段树,每颗线段树都是前一棵线段树加一个或多个变化得到的,线段树之间有某种顺序上的关系。
然后利用线段树之间的关系(很多时候是分别询问,然后做差),可以解决很多新的问题,可以优化时间和空间复杂度。
假设维护n个点,有n个操作,那么跟普通线段树相比,使用主席树不会增加时间复杂度,都是O(nlogn),而空间复杂度也只是从O(n)增加到O(nlogn)而已。
关于区间第K大的问题。
静态问题可以使用主席树。
缺点是只能解决静态的问题,优点是时空复杂度低。
动态问题可以使用树套树或整体二分。
树套树和主席树相比,优点是能解决动态问题,缺点是时空复杂度高,特别是空间复杂度高。
关于本题的话,就是主席树的扩展使用。
第i棵线段树可以查询前i个数,位于特定范围的数的个数。
版本更新就是插入第i+1个数,并得到新版本,同时保留历史版本。
最后如果询问区间[l,r]的第k大。
那就考虑T[r]和T[l-1]两颗线段树,两颗线段树维护的值一相减就得到[l,r]这个区间,位于特定范围的数的个数。(可以理解为得到了一个新的线段树,这颗线段树维护了区间[l,r]上位于特定范围的数的个数)我们就假装有那么一颗线段树,然后再递归找到第k大的数是多大。
讲的具体一点。
假装就有这么一颗线段树,维护了数轴上一段范围的数的个数。
我一开始在根,所以看到了所有数的个数。
把数轴分成两半,左子树维护者左半边,右子树维护着右半边。
但是我要找第k大,我要考虑往左子树或者右子树走。
如果左子树中数的个数大于等于k,那答案就肯定在左子树中,我就往左走。
否则左子树肯定有x个数,且x<k,那答案就一定在右子树中,我们就往右走,然后找第k-x大的数。
直到到达了一个节点,只维护了一种数,那这个数就是答案了。
代码
#include<stdio.h>#include<algorithm>#define m ((l+r)>>1)using namespace std;const int maxn = 100010;int n,q;int a[maxn];int b[maxn];int N;//////////////////////////主席树int rt[maxn],ls[maxn<<5],rs[maxn<<5],tree[maxn<<5],tot;/*void init(int n){ tot=0; for(int i=0;i<=n;i++) rt[i]=0;}*/void build(int l,int r,int& now){ now=++tot; tree[now]=0; if(l==r) return; build(l,m,ls[now]); build(m+1,r,rs[now]);}void update(int last,int l,int r,int& now,int pos){ now=++tot; ls[now]=ls[last]; rs[now]=rs[last]; tree[now]=tree[last]+1; if(l==r) return; if(pos<=m) update(ls[last],l,m,ls[now],pos); else update(rs[last],m+1,r,rs[now],pos);}int qry(int a,int b,int k){ int t1=rt[a-1]; int t2=rt[b]; int l = 1; int r = N; while(l<r) { int num = tree[ls[t2]]-tree[ls[t1]]; if(num>=k) { t1=ls[t1]; t2=ls[t2]; r=m; } else { t1=rs[t1]; t2=rs[t2]; l=m+1; k-=num; } } return l;}//////////////////////////主席树void read(){ scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",a+i); b[i]=a[i]; }}void solve(){ read(); sort(b+1,b+1+n); N = unique(b+1,b+1+n)-b-1; tot=0; build(1,N,rt[0]); for(int i=1;i<=n;i++) update(rt[i-1],1,N,rt[i],lower_bound(b+1,b+1+N,a[i])-b); int l,r,k; while(q--) { scanf("%d %d %d",&l,&r,&k); printf("%d\n",b[qry(l,r,k)]); }}int main(){ int T; scanf("%d",&T); while(T--) solve(); return 0;}
- HDU 2665 Kth number (主席树)
- hdu 2665 Kth number(主席树)
- 主席树(Kth number,HDU 2665)
- HDU 2665-Kth number(主席树)
- HDU 2665 Kth number(主席树)
- HDU 2665 Kth number 主席树
- HDU 2665 kth number(主席树)
- Hdu-2665 Kth number(主席树)
- HDU-2665 Kth number(主席树)
- hdu 2665 Kth number(主席树模板)
- hdu 2665 Kth number 主席树
- [HDU 2665] Kth number (主席树入门)
- HDU 2665 Kth number 主席树,函数式线段树
- 主席树 —— HDU 2665 Kth number
- HDU 2665 Kth number 主席树模板题
- [Hdu] 2665 Kth number(主席树模板题)
- HDU 2665 Kth number 主席树裸题
- HDU 2665 Kth number(区间第K大) (离散化+主席树)
- day09 集合 list Arraryist LinkedList Vector HashSet treeSet comparable Iterator 如有错多多指教
- 杭电oj1021 四个一周期 递归超限
- Fibonacci Again!
- [Leetcode]_36 Valid Sudoku
- hdu 多校联赛 Is Derek lying?
- 主席树(Kth number,HDU 2665)
- hdfs查看目录大小文件大小
- 【ARM】控制寄存器的几种方式
- EasyHook 中部分函数的实现分析—-申请钩子
- spring4.0之后,自定义属性编辑器的配置变化
- 获取历史和实时股票数据接口
- redis过期时间的过期策略
- 第三章maven的DOS命令创建java、web项目
- HDU1235 统计同成绩学生人数