题目链接: Dynamic Rankings
大致题意
有眼就行
解题思路
动态主席树 和 整体二分 的模板题!
关于主席树的解法, 因为有单点修改, 所以我们要采用动态主席树. 具体方法就是, 把主席树中的存储方式采用树状数组的存储方式.
因此查询的时候, 原本是传入一个l - 1版本和一个当前版本r, 但是由于采用了树状数组的存储方式, 就要传入logn个版本. 修改同理.
整体二分
不得不说, 这题整体二分跑的是真的快!
AC代码
主席树
//例题: 求动态区间第K小数 (静态树 + 动态树做法)
//思路: 用树状数组的存储方式维护前缀和. 静态树记录初始情况, 动态树维护修改情况
//时间复杂度: O((n + m)lognlogn) 空间复杂度: O(nlogn + mlognlogn)
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, M = N * 17 * 17; // M为节点数
int n, m;
int w[N];
vector<int> v(1, -0x3f3f3f3f); int len = 0; //len为离散化后值域边界, 但不是树的边界.
int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }
struct operation { //操作离线
int tp, l, r, k;
// x y N
}; vector<operation> area;
struct node {
int l, r;
int cou;
}t[M];
int rt[N], root[N], ind; //root是动态树, rt是静态树
int build(int a, int tl, int tr, int p) {
int x = ++ind; t[x] = t[p];
t[x].cou++;
if (tl == tr) return x;
int mid = tl + tr >> 1;
if (a <= mid) t[x].l = build(a, tl, mid, t[p].l);
else t[x].r = build(a, mid + 1, tr, t[p].r);
return x;
}
void modify(int a, int c, int tl, int tr, int& x) { //修改动态树
if (!x) x = ++ind;
t[x].cou += c;
if (tl == tr) return;
int mid = tl + tr >> 1;
a <= mid ? modify(a, c, tl, mid, t[x].l) : modify(a, c, mid + 1, tr, t[x].r);
}
int query(int k, int tl, int tr, vector<int>& p, vector<int>& x) { //查询树中第k小
if (tl == tr) return tl; //查询类似单树查询, 只不过p和x都变成了数组(logn棵树)
int cou = 0;
for (auto& op : x) cou += t[t[op].l].cou;
for (auto& op : p) cou -= t[t[op].l].cou;
int mid = tl + tr >> 1;
vector<int> np, nx;
if (cou >= k) {
for (auto& op : p) np.push_back(t[op].l);
for (auto& op : x) nx.push_back(t[op].l);
return query(k, tl, mid, np, nx);
}
for (auto& op : p) np.push_back(t[op].r);
for (auto& op : x) nx.push_back(t[op].r);
return query(k - cou, mid + 1, tr, np, nx); //记得减去左边贡献
}
int lowbit(int x) { return x & -x; }
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) modify(w[x], c, 1, len, root[i]);
}
int ask(int l, int r, int k) {
vector<int> np, nx;
nx.push_back(rt[r]), np.push_back(rt[l - 1]); //推入静态树
for (int i = r; i; i -= lowbit(i)) nx.push_back(root[i]);
for (int i = l - 1; i; i -= lowbit(i)) np.push_back(root[i]); //注意l - 1
return query(k, 1, len, np, nx);
}
int main()
{
cin >> n >> m;
rep(i, n) scanf("%d", &w[i]), v.push_back(w[i]);
rep(i, m) {
char s[2]; scanf("%s", s);
if (*s == 'Q') {
int l, r, k; scanf("%d %d %d", &l, &r, &k);
area.push_back({ 1, l, r, k });
}
else {
int a, c; scanf("%d %d", &a, &c);
area.push_back({ 2, a, c, NULL }); //注意存下标, 树状数组要根据位置修改
v.push_back(c);
}
}
sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());
len = v.size() - 1;
rep(i, n) { //建静态树
rt[i] = build(w[i] = find(w[i]), 1, len, rt[i - 1]);
//add(i, 1); 如果不用build, 都用add, 则为纯动态树的方法.(费空间)
}
for (auto& op : area) {
if (op.tp == 1) printf("%d\n", v[ask(op.l, op.r, op.k)]);
else {
add(op.l, -1);
w[op.l] = find(op.r); //记得修改w数组
add(op.l, 1);
}
}
return 0;
}
整体二分
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define debug(a) cout << #a << " = " << a << endl;
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
int n, m;
int w[N], res[N];
struct operation {
int tp, l, r, k, id;
// a, c, f, NULL
}; vector<operation> area;
int t[N];
int lowbit(int x) { return x & -x; }
void add(int x, int c) { for (int i = x; i <= n; i += lowbit(i)) t[i] += c; }
int ask(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res+= t[i];
return res;
}
int ask(int l, int r) { return ask(r) - ask(l - 1); }
void fact(int l, int r, vector<operation>& q) {
if (q.empty()) return;
if (l == r) {
for (auto& op : q) if (!op.tp) res[op.id] = l;
return;
}
int mid = l + r >> 1;
vector<operation> ql, qr;
for (auto& op : q) {
if (op.tp) {
if (op.r <= mid) add(op.l, op.k), ql.push_back(op);
else qr.push_back(op);
}
else {
int cou = ask(op.l, op.r);
if (cou >= op.k) ql.push_back(op);
else op.k -= cou, qr.push_back(op);
}
}
for (auto& op : ql) if (op.tp) add(op.l, -op.k);
fact(l, mid, ql), fact(mid + 1, r, qr);
}
int main()
{
cin >> n >> m;
rep(i, n) {
scanf("%d", &w[i]);
area.push_back({ 1, i, w[i], 1, NULL });
}
rep(i, m) {
res[i] = -1;
char s[2]; scanf("%s", s);
if (*s == 'Q') {
int l, r, k; scanf("%d %d %d", &l, &r, &k);
area.push_back({ 0, l, r, k, i });
}
else {
int a, c; scanf("%d %d", &a, &c);
area.push_back({ 1, a, w[a], -1, NULL });
w[a] = c;
area.push_back({ 1, a, w[a], 1, NULL });
}
}
fact(0, 0x3f3f3f3f, area);
rep(i, m) if (res[i] != -1) printf("%d\n", res[i]);
return 0;
}