Acwing255.第k小数

可持久化权值线段树

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <vector>
#define For(i, j, n) for(int i = j ; i <= n ; ++i)
using namespace std;

const int N = 1e5 + 1, M = 1e4 + 1;

struct node{
    int l, r;
    int cnt;
}tr[N * 4 + 17 * N];

int n, m, idx, a[N], root[N];
vector<int> num;

int build(int l, int r)
{
    int p = ++idx;
    tr[p].cnt = 0;
    if(l == r)
        return p;
    int mid = l + r >> 1;
    tr[p].l = build(l, mid);
    tr[p].r = build(mid + 1, r);
    return p;
}

int insert(int last, int l, int r, int tar)
{
    int now = ++idx;
    tr[now] = tr[last];
    if(l == r)
    {
        tr[now].cnt++;
        return now;
    }
    int mid = l + r >> 1;
    if(tar <= mid)
        tr[now].l = insert(tr[last].l, l, mid, tar);
    if(tar > mid)
        tr[now].r = insert(tr[last].r, mid + 1, r, tar);
    tr[now].cnt = tr[tr[now].l].cnt + tr[tr[now].r].cnt;
    return now;
}

int query(int L, int R, int l, int r, int k)
{
    if(l == r) 
        return l;
    int mid = l + r >> 1;
    int c = tr[tr[R].l].cnt - tr[tr[L].l].cnt;
    if(c < k)
        return query(tr[L].r, tr[R].r, mid + 1, r, k - c);
    else
        return query(tr[L].l, tr[R].l, l, mid, k);
}

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());
    root[0] = build(0, num.size() - 1);
    for(int i = 1; i <= n; i++)
        root[i] = insert(root[i - 1], 0, num.size() - 1, lower_bound(num.begin(), num.end(), a[i]) - num.begin());
    int l, r, k;
    while(m--)
    {
        scanf("%d%d%d", &l, &r, &k);
        printf("%d\n", num[query(root[l - 1], root[r], 0, num.size() - 1, k)]);
    }
    return 0;
}

一开始粗心大意,写错了几个细节,卡了一会:

1.tr的声明:一开始按照习惯写成了tr[N << 2 + 17 * N],这样子会因为优先级的问题,开出过大的数组;

2.build的时候,记得要先把cnt置为0,正确初始化;

3.insert完以后记得pushup(即用儿子的信息更新当前节点信息)

另外,如果这道题问的是“[l,r]区间内有多少个数小于等于ki”,那么就可以用这个链接里的离线方法来做:

(离线/树状数组)区间小于等于k的个数 - 知乎 (zhihu.com)

只要是满足区间可加性的问题,都可以用上面的方法来维护,但是这道问题问的区间内第k小数并不满足这个性质。

posted @ 2024-03-13 16:37  Gold_stein  阅读(1)  评论(0编辑  收藏  举报