• 主席树(可持久化线段树 )


    给一个长为n的序列,m次询问,每次询问[l, r]内第k大的数是几。 n <= 100000, m <= 5000

    比如有一个数组n个数据,那么这里记录的是从左往右,每个数据对应的线段树。最后的效果是两个数据的线段树对应值的差值。
    而每个数据点(排序去重后所在坐标位置)对一个的线段树记录的是当前对应区间的个数sum(i) (i是区间标号)
    举个栗子:
    7 1
    1 5 2 6 3 7 4
    2 5 3
    ans=5

    update是将这个数据所在的数组位置(不是真值的数据大小排序,所以这里需要一个转换操作)向上走的每个位置sum都+1
    用一个新数组记录原数组数据从小到大无重复的数据,然后遍历原数组的时就只要lowerbound查找此数据值对应的位置即可

    查询区间[x,y]第k大数的操作为:
    1.先抓取x-1与y这两点,两者对应线段树值dt=sum(y)-sum(x-1)作差
    2.若k>dt,则说明对应值在右子树中;反之,若k<=dt,则说明在左子树中。(此环节用递归实现即可)

    上代码:

    const int N = 200010;
    int n, q, m, cnt = 0;
    int a[N], b[N], T[N];
    int sum[N<<5], L[N<<5], R[N<<5];
    
    inline int build(int l, int r)
    {
        int rt = ++ cnt;
        sum[rt] = 0;
        if (l < r){
            L[rt] = build(l, mid);
            R[rt] = build(mid+1, r);
        }
        return rt;
    }
    
    inline int update(int pre, int l, int r, int x)
    {
        int rt = ++ cnt;
        L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre]+1;
        if (l < r){
            if (x <= mid) L[rt] = update(L[pre], l, mid, x);
            else R[rt] = update(R[pre], mid+1, r, x);
        }
        return rt;
    }
    
    inline int query(int u, int v, int l, int r, int k)
    {
        if (l >= r) return l;
        int x = sum[L[v]] - sum[L[u]];
        if (x >= k) return query(L[u], L[v], l, mid, k);
        else return query(R[u], R[v], mid+1, r, k-x);
    }
    
    int main()
    {
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i ++){
            scanf("%d", &a[i]);
            b[i] = a[i];
        }
        sort(b+1, b+1+n);
        m = unique(b+1, b+1+n)-b-1;
        T[0] = build(1, m);
        for (int i = 1; i <= n; i ++){
            int t = lower_bound(b+1, b+1+m, a[i])-b;
            T[i] = update(T[i-1], 1, m, t);
        }
        while (q --){
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            int t = query(T[x-1], T[y], 1, m, z);
            printf("%d
    ", b[t]);
        }
  • 相关阅读:
    订阅发布模式概念基础
    HTML与CSS之display:none
    HTML与CSS之导航栏
    微信小程序-笔记
    基于设计模式的用户管理
    微信小程序-开发组件-笔记1
    JavaScript异常处理
    微信小程序开发-笔记
    封装类实现增删改查
    脚本方式实现数据库增删改查
  • 原文地址:https://www.cnblogs.com/planche/p/9380208.html
Copyright © 2020-2023  润新知