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

    主席树,又叫可持久化权值线段树,其中的每一棵树都是权值线段树。

    所谓的权值线段树,就是指线段树的叶子节点保存的是当前值的个数。

    以下是线段树的经典问题,求区间第k大。

    洛谷P3834:https://www.luogu.org/problemnew/show/P3834

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 2e5+5;
    
    struct node {
        int l, r, s;    //l是存取左子树l在数组tree中的位置
                        //r是存取右子树r在数组tree中的位置
                        //s则是当前区间中存在值的数量
    }tree[maxn * 40];
    
    vector<int> v;    //用来存取离散化的数组
    
    int a[maxn];    
    int cnt;
    int root[maxn];    //root是用来存取每每颗树的根节点
    
    //返回当前值离散化后的值
    int getId(int x) {
        return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
    }
    
    //l,r是区间左右端点,x是前一个树的根节点,y是当前树的根节点,pos是需要添加的值,
    //其中&y是为了更新根节点中的左子树或右子树的值,确定他们在tree中所在的位置
    void update(int l, int r, int x, int &y, int pos) {
        y = ++cnt;
        tree[y] = tree[x];    //copy前一个树
        tree[y].s++;    //将这个树上pos所在的所有区间值加1
        if(l == r) {
            return;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) {
            update(l, mid, tree[x].l, tree[y].l, pos);
        } else {
            update(mid + 1, r, tree[x].r, tree[y].r, pos);
        }
    }
    
    //l,r是区间左右端点,x表示区间[1, left - 1],y表示区间[1, right],num是所求区间内第几大
    int query(int l, int r, int x, int y, int num) {
        if(l == r) {
            return l;
        }
        int mid = (l + r) >> 1;
        int sum = tree[tree[y].l].s - tree[tree[x].l].s;     //将两个区间的左子树相减算出区间[left, right]中左子树的范围
        //下面是判断num在左子数还是右子树里面
        if(sum >= num) {
            return query(l, mid, tree[x].l, tree[y].l, num);
        } else {
            return query(mid + 1, r, tree[x].r, tree[y].r, num - sum);    //此处千万要切记num - sum,右子树里并不包含左子树里的结点
        }
    }
    
    int main() {
        int n, m;
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            v.push_back(a[i]);
        }
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());    //离散化操作
        int ed = v.size();    //获取最大的区间长度
        for(int i = 1; i <= n; i++) {
            update(1, ed, root[i - 1], root[i], getId(a[i]));
        }
        while(m--) {
            int left, right, k;
            scanf("%d %d %d", &left, &right, &k);
            int pos = query(1, ed, root[left - 1], root[right], k);
            cout << v[pos - 1] << endl;    //还原离散化
        }
        return 0;
    }
  • 相关阅读:
    4.7字符串
    4.5 基本类型和运算符
    4.4 变量
    4.6 字符串
    hp
    openstack newton linuxbridge 改成 ovs
    理解裸机部署过程ironic
    csredis base usage
    redisclient can not connect
    Linux Install redis
  • 原文地址:https://www.cnblogs.com/buhuiflydepig/p/11231597.html
Copyright © 2011-2022 走看看