• 主席树入门


    主席树入门

    总结

    • 主席树 = 可持久化权值线段树;权值线段树是指线段树节点记录的是区间元素出现的频次,而可持久化则是通过记录历史版本来实现对历史情况的查询.可以用于静态的查询区间top k问题

    核心操作

    • 插入元素; 这意味着一个新的版本,需要生成沿查询路径的新的节点,这些节点的频次++
    • 查询操作: 类似线段树的查询,不过是在不同的两个版本之间
    • 注意点:
    1. 可以在每次插入才生成新的节点,而不是一开始就生成一棵完整的树;这样做能节省空间
    2. 堆式写法的线段树似乎难以适应新生成节点的情况,可以采用下面模板的写法;在需要的时候才进行开点
    3. 空间开到(O(4*n+nlogn))应该没有问题,一次询问时间复杂度(O(logn))

    模板题

    模板
    #include<bits/stdc++.h>
    using namespace std;
    #define db(x) cout<<"["<<#x<<"]="<<x<<endl
    //静态序列 动态区间的第k小值
    
    const int MAXN = 200005;
    //2e5 nlogn = 20n 
    int N,M;
    struct Node {
        int lch, rch, val;//val记录频次,lch,rch 记录左右儿子
    } tree[MAXN << 5];//32n
    int tot = 0, rt[MAXN];//记录根的
    vector<int> num;
    int a[MAXN];
    
    inline void insert(int &o, int l, int r, int x) {
        tree[++tot] = tree[o];
        o = tot;//新节点
        tree[o].val++;
        if(l==r) return ;//递归结束
        int mid = (l+r)>>1;
        if(x<=mid) insert(tree[o].lch,l,mid,x);
        else insert(tree[o].rch,mid+1,r,x);
        
    }
    
    inline int query(int o1, int o2, int l, int r, int k) {
        if(l==r) return l;
        int sz = tree[o2].val - tree[o1].val;
        int lsz = tree[tree[o2].lch].val - tree[tree[o1].lch].val;
        int mid = (l+r)>>1;
        if(lsz>=k) return query(tree[o1].lch,tree[o2].lch,l,mid,k);
        else return query(tree[o1].rch,tree[o2].rch,mid+1,r,k-lsz);
        
    }
    int main(){
        scanf("%d %d",&N,&M);
        for(int i=1;i<=N;i++){scanf("%d",a+i);num.push_back(a[i]);}
        sort(num.begin(),num.end());
        num.erase(unique(num.begin(),num.end()),num.end());
        /*
        for(auto t:num){
            cout<<t<<endl;
        }
        */
        int sz = num.size();
        for(int i=1;i<=N;i++){
            int t = lower_bound(num.begin(),num.end(),a[i])-num.begin()+1;
            //db(t);
            rt[i] = rt[i-1];
            //printf("insert %d
    ",t);
            insert(rt[i],1,sz,t);        
        }
        int l,r,k;
        for(int i=1;i<=M;i++){
            scanf("%d %d %d",&l,&r,&k);
            printf("%d
    ",num[query(rt[l-1],rt[r],1,sz,k)-1]);
        }
        return 0;
    
    }
    
    
  • 相关阅读:
    JavaScript教程——JavaScript 的基本语法(标识符)
    ECMAScript 6 入门——ES6 声明变量的六种方法
    JavaScript教程——数据类型概述
    对称机密算法与非对称机密算法
    C语言提高 (2) 第二天 用指针对字符串进行操作
    C语言提高 (1) 第一天 数据类型本质与内存四区
    关于内存地址和内存空间的理解(转)
    为什么对数组处理的函数需要传递数组长度
    Unicode编码,解释UCS、UTF、BMP、BOM等名词
    上海
  • 原文地址:https://www.cnblogs.com/fridayfang/p/11098751.html
Copyright © 2020-2023  润新知