主席树

传送门

点击查看代码
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);
#define endl '\n'
using namespace std;

typedef long long ll;
const int MAXN = 2e5 + 10;
int n, m, cnt = 0;  //cnt代表当前的节点编号,这里用到了动态开点的思想
int a[MAXN], b[MAXN], re[MAXN], head[MAXN];    //原数组, b和re数组mp反映射数组, 每个历史版本的线段树的头节点
map<int, int> mp;   //离散化
struct Node
{
    int l, r, val, lson, rson;  //l, r权值区间内有多少个数val,当前节点的左儿子,当前节点的右儿子
} tree[MAXN * 30];  //主席树一般开30,不够再往上10倍开

void build(int l, int r, int cur)
{
    tree[cur].l = l, tree[cur].r = r, tree[cur].val = 0;
    if (l == r)
        return;
    tree[cur].lson = ++cnt; tree[cur].rson = ++cnt; //动态开点的思想
    int mid = l + r >> 1;
    build(l, mid, tree[cur].lson), build(mid + 1, r, tree[cur].rson);
}

void pushup(int cur)
{
    tree[cur].val = tree[tree[cur].lson].val + tree[tree[cur].rson].val;
    return;
}

void update(int tar, int cur, int pre)  //要更新的点建立一棵在前一个版本的基础上建立一棵新树
{
    tree[cur].l = tree[pre].l, tree[cur].r = tree[pre].r, tree[cur].val = tree[pre].val;    //肯定在前一个版本的基础上,所以先初始化
    if (tree[cur].l == tree[cur].r) //准确的到那个数了,就对那个数的权值区间个数++
    {
        tree[cur].val++;
        return;
    }
    int mid = tree[cur].l + tree[cur].r >> 1;
    if (tar <= mid) //说明要更新的点在左子树
    {
        tree[cur].lson = ++cnt; //动态开点左子树
        tree[cur].rson = tree[pre].rson;    //右侧的节点不变
        update(tar, tree[cur].lson, tree[pre].lson);    //继续更新左子树
    }
    else    //说明要更新的点在右子树
    {
        tree[cur].lson = tree[pre].lson;    //左侧的节点不变
        tree[cur].rson = ++cnt; //动态开点右子树
        update(tar, tree[cur].rson, tree[pre].rson);    //继续更新有子树
    }
    pushup(cur);
}

int query(int k, int cur, int pre)
{
    if (tree[cur].l == tree[cur].r) //当前左右区间相等,必然就是这个区间里面第k小的数
        return tree[cur].l;
    int val = tree[tree[cur].lson].val - tree[tree[pre].lson].val;
    if (k <= val)   //说明这个第k小在左子树
        return query(k, tree[cur].lson, tree[pre].lson);
    else    //说明这个第k小的在右子树
        return query(k - val, tree[cur].rson, tree[pre].rson);  //因为左子树已经有val个了,所以这里要先减去val个    
}

int main()
{
    IOS; cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i], mp[a[i]] = 1;
    int pos = 0;
    for (auto it = mp.begin(); it != mp.end(); ++it)    //离散化处理
        it->second = ++pos, re[pos] = it->first;
    for (int i = 1; i <= n; ++i)
        b[i] = mp[a[i]];
    head[0] = 1, cnt = 1;
    build(1, mp.size(), head[0]);   //先建立一棵空树
    for (int i = 1; i <= n; ++i)
    {
        head[i] = ++cnt;    //每一个数对应每一个更新版本,每一个更新版本对应一个cnt, 代表这个版本的头
        update(mp[a[i]], head[i], head[i - 1]);
    }
    int l, r, k;
    while (m--)
    {
        cin >> l >> r >> k;
        cout << re[query(k, head[r], head[l - 1])] << endl; //第r个版本和l - 1个历史版本进行计算
    }
    return 0;
}

posted @ 2022-11-03 21:07  YUGUOTIANQING  阅读(24)  评论(0)    收藏  举报