静态主席树就是求某个区间第k大,而动态主席树就需要一边查询区间第k大,还要一边进行单点更新。
学习动态主席树需要了解的知识:静态主席树,数组数组,离线操作。
动态主席树的结构:树状数组套主席树 + 静态主席树。
题目:洛谷P2617
https://www.luogu.org/problem/P2617
代码:
#include <iostream> #include <cstdio> #include <vector> #include <algorithm> using namespace std; const int maxn = 5e5+7; vector<int> v; //存储离散化后的数组 struct Query { int l, r, k; bool op; }Q[maxn]; //提前存储需要用到的指令,便于之后的离散化 int root[maxn * 40]; //存储静态主席树的根节点 int T[maxn * 40]; //存储动态主席树的根节点 int arr[maxn]; //存储输入的N个值 int lson[maxn * 40], rson[maxn * 40]; //存储左右子树的结点 int ls[maxn * 40], rs[maxn * 40]; //存储更新时用到的值 int sum[maxn * 40]; //存储某个区间的值的个数 int cnt; int size; //区间的长度大小 int n, m; int lowbit(int x) { return x & (-x); } int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; } void build(int l, int r, int &cur) { cur = ++cnt; sum[cur] = 0; if(l == r) { return; } int mid = (l + r) >> 1; build(l, mid, lson[cur]); build(mid + 1, r, rson[cur]); } void update(int l, int r, int pre, int &cur, int pos, int val) { cur = ++cnt; lson[cur] = lson[pre]; rson[cur] = rson[pre]; sum[cur] = sum[pre] + val; if(l == r) { return; } int mid = (l + r) >> 1; if(pos <= mid) { update(l, mid, lson[pre], lson[cur], pos, val); } else { update(mid + 1, r, rson[pre], rson[cur], pos, val); } } void change(int x, int pos, int val) { while(x <= n) { update(1, size, T[x], T[x], pos, val); x += lowbit(x); } } int query(int l, int r, int pre, int cur, int k) { if(l == r) { return l; } int mid = (l + r) >> 1; int res = sum[lson[cur]] - sum[lson[pre]]; //此处时静态主席树中所求区间[left, right]中的值 //下面两个for循环时求动态主席树中所求区间[left, right]中的值 for(int i = 1; i <= ls[0]; i++) { res -= sum[lson[ls[i]]]; } for(int i = 1; i <= rs[0]; i++) { res += sum[lson[rs[i]]]; } //解释:静态主席树中的值是原来N个数的值,而动态主席树中是之后更新过后的值,需要将两个相加起来才能求得总值 if(res >= k) { //下面两个for循环是将每个结点得左子树更新到数组中去 for(int i = 1; i <= ls[0]; i++) { ls[i] = lson[ls[i]]; } for(int i = 1; i <= rs[0]; i++) { rs[i] = lson[rs[i]]; } return query(l, mid, lson[pre], lson[cur], k); } else { //同上,更新右子树 for(int i = 1; i <= ls[0]; i++) { ls[i] = rson[ls[i]]; } for(int i = 1; i <= rs[0]; i++) { rs[i] = rson[rs[i]]; } return query(mid + 1, r, rson[pre], rson[cur], k - res); } } int main() { scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &arr[i]); v.push_back(arr[i]); } for(int i = 0; i < m; i++) { char str[5]; scanf("%s", str); if(str[0] == 'Q') { scanf("%d %d %d", &Q[i].l, &Q[i].r, &Q[i].k); Q[i].op = true; } else { scanf("%d %d", &Q[i].l, &Q[i].r); Q[i].op = false; v.push_back(Q[i].r); } } sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); //基本离散化操作 size = v.size(); //获取区间长度 build(1, size, root[0]); for(int i = 1; i <= n; i++) { //创建静态主席树 update(1, size, root[i - 1], root[i], find(arr[i]), 1); } for(int i = 1; i <= n; i++) { //初始化动态主席树 T[i] = root[0]; } for(int i = 0; i < m; i++) { if(Q[i].op) { //下面两个for循环是在树状数组中获取在所求区间中得有关根节点 ls[0] = rs[0] = 0; for(int j = Q[i].l - 1; j > 0; j -= lowbit(j)) { ls[++ls[0]] = T[j]; } for(int j = Q[i].r; j > 0; j -= lowbit(j)) { rs[++rs[0]] = T[j]; } int pos = query(1, size, root[Q[i].l - 1], root[Q[i].r], Q[i].k); printf("%d ", v[pos - 1]); } else { //第一次是将原有得值删掉,第二次是添加新得值 change(Q[i].l, find(arr[Q[i].l]), -1); arr[Q[i].l] = Q[i].r; //更新原数组中得值 change(Q[i].l, find(Q[i].r), 1); } } return 0; }