可持久化权值线段树
#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小数并不满足这个性质。
标签:cnt,include,Acwing255,int,tr,mid,now,小数 From: https://www.cnblogs.com/smartljy/p/18070966