zoukankan      html  css  js  c++  java
  • 主席树备忘

    主席树备忘

    非堆式动态建树

    传统线段树都是通过(cur<<1)(cur<<1|1)分别访问左右儿子,而非堆式建树使用(L[])(R[])访问左右儿子,儿子节点编号动态使用(++topt)分配(如分配(cur)新的一个左二子节点:L[cur] = ++topt;)

    主席树为了节省空间,就是通过这种建树方式建树的

    void build(int &cur, ...){
      if(cur==0)
      	cur = ++topt;
    	//...
    }
    

    主席树

    主席树为每一个历史节点都新建了一棵线段树以供随时查询历史版本,但是除了第一棵是完全二叉树,其余都是不完全的线段树(如果每个历史节点全部新建就会MLE),主席树会共享没有改变过的节点。

    记得存下每个历史节点的线段树的根节点以供访问。

    洛谷 P3834 模板

    题面,题意摘要:给定序列,每次询问区间([l,r])的的第(k)小值

    考虑依次对插入的数,每个数都建一棵值域线段树,树节点维护一个区间([a,b])即在第(a)小到第(b)小范围内目前数的个数。依输入顺序插入数,并查询其排名(所以最好离散化一下),在这个当前新树下,将所有包含这个数的区间都+1

    查询区间([l,r])时,用前缀和思想,将第(r)个数的线段树减去第(l-1)个数,得出一棵包含区间([l,r])内各数分布情况的树,再像Splay那样,每次查询小于当前范围数的个数,如果大于(k),则第(k)个数一定在前面那个范围里,反之,则在后面那个区间里。

    关于空间复杂度,除了第一棵最大(4n)个节点,其余线段树每次只会最多新建(log^n_2)个节点,总空间复杂度为(4n+(n-1)log^n_2)(相对于原来的(4n^2))

    AC Code

    #include <cstdio>
    #include <algorithm>
    #define MAXN 200010
    #define MAXM MAXN*20
    using namespace std;
    int n,q,sz,L[MAXM],R[MAXM],val[MAXM];
    int a[MAXN],s[MAXM],his[MAXN];
    int topt;
    int query(int a, int b, int l, int r, const int k){
        if(l==r) return l;
        int sz = val[L[b]]-val[L[a]];
        int mid = (l+r)>>1;
        if(sz>=k) return query(L[a], L[b], l, mid, k);
        else return query(R[a], R[b], mid+1, r, k-sz);
    }
    void add(int &cur, const int pre, int l, int r, int x){
        cur=++topt;
        L[cur]=L[pre], R[cur]=R[pre],val[cur]=val[pre]+1;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) add(L[cur], L[pre], l, mid, x);
        else add(R[cur], R[pre], mid+1, r, x);
    }
    void buildt(int &cur, int l, int r){
        cur=++topt;
        if(l<r){
            int mid = (l+r)>>1;
            buildt(L[cur], l, mid);
            buildt(R[cur], mid+1, r);
        }
    }
    int main(){
        scanf("%d %d", &n, &q);
        for(register int i=1;i<=n;++i)
            scanf("%d", &a[i]), s[i]=a[i];
        sort(s+1, s+1+n);
        sz = unique(s+1, s+1+n) - (s+1);
        buildt(his[0], 1, sz);
        for(register int i=1;i<=n;++i){
            int t = lower_bound(s+1, s+1+sz, a[i]) - s;
            add(his[i], his[i-1], 1, sz, t);
        }
        while(q--){
            int l,r,k;
            scanf("%d %d %d", &l, &r, &k);
            printf("%d
    ", s[query(his[l-1], his[r], 1, sz, k)]);
        }
        return 0;
    }
    

    洛谷 P3919 模板

    题面,真·模板题

    切掉上面那道假模板后,此题顿觉轻松

    #include <cstdio>
    #define MAXN 1000001
    #define MAXM (20*MAXN)
    #define mid ((l+r)>>1)
    #define SAFE(A) ((A)<0?0:(A))
    using namespace std;
    int n,m,tot,a[MAXN],his[MAXN];
    int L[MAXM],R[MAXM],V[MAXM];
    void buildt(int &cur, int l, int r){
        cur=++tot;
        if(l==r){
            V[cur]=a[l];
            return;
        }
        buildt(L[cur], l, mid);
        buildt(R[cur], mid+1, r);
    }
    void change(int &cur, int pre, int x, int val, int l, int r){
        cur=++tot;
        L[cur]=L[pre],R[cur]=R[pre],V[cur]=V[pre];
        if(l==r){
            V[cur]=val;
            return;
        }
        if(x<=mid) change(L[cur], L[pre], x, val, l, mid);
        else change(R[cur], R[pre], x, val, mid+1, r);
    }
    int query(int cur, int x, int l, int r){
        if(l==r){
            return V[cur];
        }
        if(x<=mid) return query(L[cur], x, l, mid);
        return query(R[cur], x, mid+1, r);
    }
    int main()
    {
        scanf("%d %d", &n, &m);
        for(register int i=1;i<=n;++i) scanf("%d", &a[i]);
        buildt(his[0], 1, n);
        for(register int i=1;i<=m;++i){
            int ver,opt;scanf("%d %d", &ver, &opt);
            if(opt==1){
                int x,val;scanf("%d %d", &x, &val);
                change(his[i], his[ver], x, val, 1, n);
            }else{
                int x;scanf("%d", &x);
                his[i]=his[ver];
                printf("%d
    ", query(his[ver], x, 1, n));
            }
        }
        return 0;
    }
    
  • 相关阅读:
    高性能的序列化与反序列化:kryo的简单使用
    C/C++读写csv文件(用getline探测逗号分隔符)
    VS2012中使用CEGUI项目发布到XP平台的问题(核心方法就一句话。“你项目使用的所有外部依赖库都用/MT编译。”)
    C/C++使用libcurl库发送http请求(get和post可以用于请求html信息,也可以请求xml和json等串)
    C/C++使用openssl进行摘要和加密解密(md5, sha256, des, rsa)
    DOM解析xml实现读、写、增、删、改
    Go and JSON
    HTTP2.0
    全球化与本地化
    远程调试 Azure 上的 Website
  • 原文地址:https://www.cnblogs.com/santiego/p/10799957.html
Copyright © 2011-2022 走看看