• 主席树入门(转载)


    转载自【学习笔记】主席树 最详细的主席树(不修改,待修改) BZOJ 1901

    主席树的思想,个人感觉在于线段树动态开点和前缀和。详细的过程,上面的两个博客感觉讲得挺好,就不重复,直接上两个入门题。

    P3567 [POI2014]KUR-Couriers

    区间第K大。

    直接以权值线段树来建主席树,然后根据前缀和,就可以知道对应的范围的主席树内有多少数,然后找第K大即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e5+11;
    struct Tree{
        int lson,rson,sum;
    }T[N*32];
    int tn,a[N],root[N];
    int build(int l,int r){
        int cur=++tn,mid=(l+r)>>1;
        T[cur].sum=0;
        if(l==r) return cur;
        T[cur].lson=build(l,mid);
        T[cur].rson=build(mid+1,r);
        return cur;
    }
    int addT(int x,int l,int r,int p){
        int cur=++tn,mid=(l+r)>>1;
        T[cur]=T[x];
        T[cur].sum++;
        if(l==r) return cur;
        if(p<=mid) T[cur].lson=addT(T[x].lson,l,mid,p);
        else T[cur].rson=addT(T[x].rson,mid+1,r,p);
        return cur;
    }
    int query(int L,int R,int l,int r,int num){
        if(l==r) return l;
        int mid=(l+r)>>1;
        int lsum=T[T[R].lson].sum-T[T[L].lson].sum;
        int rsum=T[T[R].rson].sum-T[T[L].rson].sum;
        if(lsum>=num) return query(T[L].lson,T[R].lson,l,mid,num);
        if(rsum>=num) return query(T[L].rson,T[R].rson,mid+1,r,num);
        return 0;
    }
    int main(){
        int n,m;
        while(~scanf("%d%d",&n,&m)){
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            root[0]=build(1,n);
            for(int i=1;i<=n;i++) root[i]=addT(root[i-1],1,n,a[i]);
            int l,r;
            while(m--){
                scanf("%d%d",&l,&r);
                printf("%d
    ",query(root[l-1],root[r],1,n,(r-l+1)/2+1));
            }
        }
        return 0;
    } 
    KKKKK

    D-query

    区间内不同的数的个数。

    这里把查询的区间离线下来,然后用线段树或者树状数组维护也可,思路便是记录每个数出现的上一个位置pos,然后从左往右遍历的时候,对于ai来说,i位置+1,pos[ai]位置-1,这样便使得查询的每个区间里的每个相同的数都只在最右边的位置被计算到,当每个碰到一个查询区间的右边界时,去查找答案即可。

    那主席树的做法的话,不再像上题一样以权值线段树来建主席树,而是以区间来建主席树。思路的话也是类似线段树的做法,记录每个数出现的上一个位置pos,然后在新建一棵修改对i处+1的主席树前,先建一棵对pos[a[i]]处-1的主席树,当然,两者的顺序可以反过来。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e4+11,M=1e6+11;
    struct Tree{
        int lson,rson,val;
    }T[N*32];
    int tn,a[N],root[N],vis[M];
    int build(int l,int r){
        int cur=++tn,mid=(l+r)>>1;
        T[cur].val=0;
        T[cur].lson=T[cur].rson=0;
        if(l==r) return cur;
        T[cur].lson=build(l,mid);
        T[cur].rson=build(mid+1,r);
        return cur;
    }
    int addT(int x,int l,int r,int p,int val){
        int cur=++tn,mid=(l+r)>>1;
        T[cur]=T[x];
        T[cur].val=T[x].val+val;
        if(l==r) return cur;
        if(p<=mid) T[cur].lson=addT(T[x].lson,l,mid,p,val);
        else T[cur].rson=addT(T[x].rson,mid+1,r,p,val);
        return cur;
    }
    int query(int x,int l,int r,int ql){
        if(l>=ql) return T[x].val;
        int ans=0,mid=(l+r)>>1;
        if(ql<=mid) ans+=query(T[x].lson,l,mid,ql);
        ans+=query(T[x].rson,mid+1,r,ql);
        return ans;
    }
    int main(){
        int n;
        while(~scanf("%d",&n)){
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                vis[a[i]]=0;
            }
            tn=0;
            root[0]=build(1,n);
            for(int i=1;i<=n;i++){
                int tempr=root[i-1];
                if(vis[a[i]]) tempr=addT(tempr,1,n,vis[a[i]],-1);
                root[i]=addT(tempr,1,n,i,1);
                vis[a[i]]=i;
            }
            int m,l,r;
            scanf("%d",&m);
            while(m--){
                scanf("%d%d",&l,&r);
                printf("%d
    ",query(root[r],1,n,l));
            }
        }
        return 0;
    }
    DDDDD
  • 相关阅读:
    反编译
    字符编码集格式
    BZOJ 1032 [JSOI2007]祖码Zuma
    2015-7-21 模板练习
    2015-7-20 模板练习
    BZOJ 1028 [JSOI2007]麻将
    BZOJ 1027 [JSOI2007]合金
    BZOJ 1026 [SCOI2009]windy数
    BZOJ 1025 [SCOI2009]游戏
    COJ 2024 仙境传奇(五)——一个天才的觉醒 素数筛
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/13269601.html
Copyright © 2020-2023  润新知