题意
题解
BIT + 主席树
求动态区间第 k k k 小,带修改的主席树裸题。考虑主席树修改某一个历史版本(即某一个前缀的权值线段树),那么也需要修改其后的历史版本,单次修改复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
B I T BIT BIT 单次修改前缀复杂度 O ( l o g n ) O(logn) O(logn),那么可以 B I T BIT BIT 套主席树。基本思想是 B I T BIT BIT 维护的区间为历史版本 [ 1 , n ] [1,n] [1,n],那么可以高效的进行单点修改; B I T BIT BIT 上每一个点对应一颗主席树,维护值域 [ 0 , u b ) [0,ub) [0,ub) 的信息,那么可以处理区间第 k k k 小的查询;此时主席树维护的不再是 a i a_i ai 的前缀信息,而是对应的 B I T BIT BIT 点的索引值二进制所代表区间的信息。单点修改,即 B I T BIT BIT 处理前缀同时更新主席树,复杂度 O ( l o g 2 n ) O(log^2n) O(log2n)。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 100005
#define tree_size 32400005
struct operation
{
bool q;
int l, r, k, x, y;
} op[maxn];
int N, M, K, A[maxn], S[maxn << 1];
int cnt, rt[maxn], cl[tree_size], cr[tree_size], sum[tree_size];
vector<int> tu, tv;
void update(int &cur, int x, int v, int l, int r)
{
if (!cur)
cur = ++cnt;
sum[cur] += v;
if (r - l > 1)
{
int m = (l + r) >> 1;
x < m ? update(cl[cur], x, v, l, m) : update(cr[cur], x, v, m, r);
}
}
void bit_update(int i, int v)
{
int x = lower_bound(S, S + K, A[i]) - S;
for (int j = i; j <= N; j += j & -j)
update(rt[j], x, v, 0, K);
}
int query(int k, int l, int r)
{
if (r - l == 1)
return l;
int s = 0, m = (l + r) >> 1;
for (int i = 0; i < (int)tu.size(); ++i)
s -= sum[cl[tu[i]]];
for (int i = 0; i < (int)tv.size(); ++i)
s += sum[cl[tv[i]]];
if (k <= s)
{
for (int i = 0; i < (int)tu.size(); ++i)
tu[i] = cl[tu[i]];
for (int i = 0; i < (int)tv.size(); ++i)
tv[i] = cl[tv[i]];
return query(k, l, m);
}
else
{
for (int i = 0; i < (int)tu.size(); ++i)
tu[i] = cr[tu[i]];
for (int i = 0; i < (int)tv.size(); ++i)
tv[i] = cr[tv[i]];
return query(k - s, m, r);
}
}
int bit_query(int u, int v, int k)
{
tu.clear();
tv.clear();
for (int j = u; j > 0; j -= j & -j)
tu.push_back(rt[j]);
for (int j = v; j > 0; j -= j & -j)
tv.push_back(rt[j]);
return query(k, 0, K);
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; ++i)
{
scanf("%d", A + i);
S[K++] = A[i];
}
for (int i = 0; i < M; ++i)
{
char c;
scanf(" %c", &c);
op[i].q = c == 'Q';
if (op[i].q)
scanf("%d%d%d", &op[i].l, &op[i].r, &op[i].k);
else
{
scanf("%d%d", &op[i].x, &op[i].y);
S[K++] = op[i].y;
}
}
sort(S, S + K);
K = unique(S, S + K) - S;
for (int i = 1; i <= N; ++i)
bit_update(i, 1);
for (int i = 0; i < M; ++i)
{
if (op[i].q)
printf("%d\n", S[bit_query(op[i].l - 1, op[i].r, op[i].k)]);
else
{
int j = op[i].x;
bit_update(j, -1);
A[j] = op[i].y;
bit_update(j, 1);
}
}
return 0;
}
整体二分
整体二分的基本思路即,将全部的查询分治处理。考虑单次查询区间第 k k k 小,可以按值域进行分治。对于多次查询,可以根据区间中值划分查询至左右区间(统计 a i a_i ai 小于值域区间中值的数量, B I T BIT BIT 维护前缀和即可)。对于 a i a_i ai 的单点修改,则按照时间顺序存储,此时将原始 a i a_i ai 看做多次修改,且时间顺序最靠前;对 a i a_i ai 原值(若 a i < m i d a_i<mid ai<mid)对应地在 B I T BIT BIT 上减一,再将修改值(若 a i < m i d a_i<mid ai<mid)在 B I T BIT BIT 上加一。将 a i a_i ai 与修改值离线下来,离散化处理,总的时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 100005
struct operation
{
bool q;
int x, y, z, i; //q = 1 : l, r, k, i; q = 0 : i, x, v
operation(bool q, int x, int y, int z, int i) : q(q), x(x), y(y), z(z), i(i) {}
};
int N, Q, M, A[maxn], B[maxn << 1], bit[maxn], res[maxn];
vector<operation> op;
void add(int i, int x)
{
while (i <= N)
{
bit[i] += x;
i += i & -i;
}
}
int sum(int i)
{
int s = 0;
while (i > 0)
{
s += bit[i];
i -= i & -i;
}
return s;
}
void solve(vector<operation> &op, int l, int r)
{
if (op.empty() || r - l == 1)
{
for (auto &q : op)
if (q.q)
res[q.i] = l;
return;
}
vector<operation> left, right;
int m = (l + r) >> 1;
for (auto &q : op)
{
int m = (l + r) >> 1;
if (q.q)
{
int s = sum(q.y) - sum(q.x - 1);
if (q.z <= s)
left.emplace_back(1, q.x, q.y, q.z, q.i);
else
right.emplace_back(1, q.x, q.y, q.z - s, q.i);
}
else
{
if (q.y < m)
{
add(q.x, q.z);
left.emplace_back(0, q.x, q.y, q.z, 0);
}
else
right.emplace_back(0, q.x, q.y, q.z, 0);
}
}
for (auto &q : left)
if (!q.q)
add(q.x, -q.z);
solve(left, l, m);
solve(right, m, r);
}
int main()
{
scanf("%d%d", &N, &Q);
for (int i = 1; i <= N; ++i)
{
scanf("%d", A + i);
B[M++] = A[i];
op.emplace_back(0, i, A[i], 1, 0);
}
for (int i = 0; i < Q; ++i)
{
char c;
scanf(" %c", &c);
bool q = c == 'Q';
if (q)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
op.emplace_back(1, l, r, k, i);
}
else
{
int x, y;
scanf("%d%d", &x, &y);
op.emplace_back(0, x, A[x], -1, 0);
B[M++] = A[x] = y;
op.emplace_back(0, x, A[x], 1, 0);
}
}
sort(B, B + M);
M = unique(B, B + M) - B;
for (auto &q : op)
if (!q.q)
q.y = lower_bound(B, B + M, q.y) - B;
memset(res, -1, sizeof(res));
solve(op, 0, M);
for (int i = 0; i < Q; ++i)
{
if (res[i] != -1)
printf("%d\n", B[res[i]]);
}
return 0;
}