主席树(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;}


原创粉丝点击