zoukankan      html  css  js  c++  java
  • 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】

    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=63740442
    向大(hei)佬(e)实力学(di)习(tou)

    主席树主要用于处理区间的问题,其中区间不一定是单纯的区间,还是可以将题目中的一些信息转化一下(如 操作、时间等),即需要查询区间信息的数据,还需要用线段树用的题目,就可以用主席树。
    以前有畏难心理,总觉得主席树好复杂,各种逃避。后来再次学习,总算是学会了基础的东西。

    主席树通常是前缀和和线段树的结合,下面以一道经典题:区间第k大 为例

    第k大,想到用值域线段树。区间可以转化为前缀,相减即可。即对每一个前缀节点建线段树,但如果暴力建,就完蛋了(^10),完美爆空间

    仔细一想,每一个线段树和前一个相比,其实只修改了一个点。我们只需要新建树根到新增叶子节点路径上的点就可以了,其他的指向前一棵树的对应位置
    放一张图来理解的更直观一些(灰色格子是虚点)
    这里写图片描述
    由此可见,空间复杂度为n*logn,可以接受
    建树代码

    void insert(Node *&ndn,Node *ndp,int le,int ri,int pos){//ndn当前的节点,注意要加&修改.ndp前一个节点,同步
        ndn=newnode();
        ndn->sum=ndp->sum+1;
        if(le==ri) return ;
        ndn->ls=ndp->ls,ndn->rs=ndp->rs;
        int mid=(le+ri)>>1;
        if(pos<=mid) insert(ndn->ls,ndp->ls,le,mid,pos);
        else insert(ndn->rs,ndp->rs,mid+1,ri,pos);
    }

    这里有一个小技巧。因为指针常常指出去,就RE了,为了避免这一点以及不需要对root[0]先花2*n的空间建空树,我们这里用一个null的自环来表示空节点。妈妈再也不用担心我的指针写挂啦~(≧▽≦)/~

    struct Node{
        Node *ls,*rs;
        int sum;
    }*root[N],*null,pool[N*50],*tail=pool;
    int main(){
        null=++tail;
        null->ls=null->rs=null;
        null->sum=0;
        root[0]=null;
    }

    然后就是查询,其实很简单,就像普通的值域线段树和前缀和区间查询就可以了

    int query(Node *ndn,Node *ndp,int le,int ri,int pos){
        if(le==ri) return le;
        int lsum=ndn->ls->sum - ndp->ls->sum;//前缀和
        int mid=(le+ri)>>1;
        if(pos<=lsum) return query(ndn->ls,ndp->ls,le,mid,pos);
        else return query(ndn->rs,ndp->rs,mid+1,ri,pos-lsum);
    }

    关键就解决了,至于区间第k大的题需要离散化,就不赘述了
    完整代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    
    const int N=100000+5;
    const int oo=1000000000+7;
    
    struct Node{
        Node *ls,*rs;
        int sum;
    }*root[N],*null,pool[N*50],*tail=pool;
    struct aa{
        int nu,val;
    }hh[N];
    int n,m,rank[N];
    
    bool cmp1(aa a,aa b){
        return a.val<b.val;
    }
    bool cmp2(aa a,aa b){
        return a.nu<b.nu;
    }
    Node *newnode(){
        Node *rt=++tail;
        rt->ls=rt->rs=null;
        rt->sum=0;
        return rt;
    }
    void insert(Node *&ndn,Node *ndp,int le,int ri,int pos){
        ndn=newnode();
        ndn->sum=ndp->sum+1;
        if(le==ri) return ;
        ndn->ls=ndp->ls,ndn->rs=ndp->rs;
        int mid=(le+ri)>>1;
        if(pos<=mid) insert(ndn->ls,ndp->ls,le,mid,pos);
        else insert(ndn->rs,ndp->rs,mid+1,ri,pos);
    }
    int query(Node *ndn,Node *ndp,int le,int ri,int pos){
        if(le==ri) return le;
        int lsum=ndn->ls->sum - ndp->ls->sum;
        int mid=(le+ri)>>1;
        if(pos<=lsum) return query(ndn->ls,ndp->ls,le,mid,pos);
        else return query(ndn->rs,ndp->rs,mid+1,ri,pos-lsum);
    }
    int main(){
        null=++tail;
        null->ls=null->rs=null;
        null->sum=0;
        root[0]=null;
    
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&hh[i].val);
            hh[i].nu=i;
        }
        sort(hh+1,hh+n+1,cmp1);
        int sz=0,tmp=0;
        for(int i=1;i<=n;i++){
            if(tmp!=hh[i].val){
                sz++;tmp=hh[i].val;
                rank[sz]=tmp;hh[i].val=sz;
            }
            else hh[i].val=sz;
        }
        sort(hh+1,hh+n+1,cmp2);
        for(int i=1;i<=n;i++)
            insert(root[i],root[i-1],1,sz,hh[i].val);
        int x,y,k;
        while(m--){
            scanf("%d%d%d",&x,&y,&k);
            printf("%d
    ",rank[query(root[y],root[x-1],1,sz,k)]);
        }
        return 0;
    }

    当然,主席树不只可以解决区间第k大的问题,灵活的变形后还可以解决不少问题。前段时间考了一套大佬的自编题,第一题就是变型的主席树,附上链接:
    http://blog.csdn.net/coco56181712/article/details/75647313

  • 相关阅读:
    java_oop_方法2
    POJ 3276 Face The Right Way(反转)
    POJ 3276 Face The Right Way(反转)
    POJ 2566 Bound Found(尺取法,前缀和)
    POJ 2566 Bound Found(尺取法,前缀和)
    POJ 3320 Jessica's Reading Problem(尺取法)
    POJ 3320 Jessica's Reading Problem(尺取法)
    POJ 3061 Subsequence(尺取法)
    POJ 3061 Subsequence(尺取法)
    HDU 1222 Wolf and Rabbit(欧几里得)
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763154.html
Copyright © 2011-2022 走看看