B. The Tortoise and the Hare
给定一个长度为 n n n的数组 a , ( 1 ≤ a i < m ) a, (1 \leq a_i < m) a,(1≤ai<m), m m m是一个给定的数 ( 1 ≤ m ≤ 1 0 9 ) (1 \leq m \leq 10 ^ 9) (1≤m≤109),有 Q Q Q次操作,分为两类:
- 给定 u , v , ( 1 ≤ u ≤ n , 1 ≤ v < m ) u, v,(1 \leq u \leq n, 1 \leq v < m) u,v,(1≤u≤n,1≤v<m),把数组上 a u a_u au的值改为 v v v。
- 给定 l , r , k , ( 1 ≤ l ≤ r ≤ n , 1 ≤ k ≤ r − l ) l, r, k, (1 \leq l \leq r \leq n, 1 \leq k \leq r - l) l,r,k,(1≤l≤r≤n,1≤k≤r−l),每次对 [ l , r ] [l, r] [l,r]区间内前 r − l + 1 − k r - l + 1 - k r−l+1−k小的数都 + 1 + 1 +1,问最多能进行多少次操作, 使得 i ∈ [ l , r ] , a i < m i \in[l, r], a_i < m i∈[l,r],ai<m恒成立。
对于操作一,我们直接修改即可,如何计算询问二呢,我们思考如下一个问题:
给定一个长度为 n n n的数组 a a a,每次我们要选择 k k k个不同的数,把这 k k k个数的值都减 1 1 1,问我们最多能进行几次操作。
这个问题考虑最优解法,其实就是每次拿前 k k k大,然后对这 k k k个数都减 1 1 1,知道不能进行操作为止。
不难发现其实这个问题,跟我们的询问二操作是几乎一样的,如果我们对询问二中的每个 a i a_i ai都存 m − a i − 1 m - a_i - 1 m−ai−1的值,这个问题就变得跟上述问题是一样的了。
考虑如何快速解决这个问题(因为我们不能对每次询问都进行模拟),尝试二分求解:
假设我们当前二分区间为 [ l , r ] [l, r] [l,r],判断答案是在 [ l , m i d ] [l, mid] [l,mid]或者 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r]区间,所以我们需要判断 x = m i d + 1 x = mid + 1 x=mid+1是否是可行的。
对于那些 a i ≥ x a_i \geq x ai≥x的数来说,如果每次删除我们都选他,可以发现对整个过程的模拟是没有影响的,假设这样的数有 c n t cnt cnt个。
接下来我们的问题就转换成为,当所有数都 < x < x <x,我们每次需要挑选 k − c n t k - cnt k−cnt个数然后对其 − 1 -1 −1,能否进行 x x x次操作了。
我们再对这个问题进行转换,如果我们要进行 x x x次操作,每次我们最多能选多少个数,如果每次可选的数 ≥ k − c n t \geq k - cnt ≥k−cnt,则说明合法。
由于 a i < x a_i < x ai<x,我们进行了 x x x次操作,说明,我们可以满足在同一次操作中不会出现两个一样的,所以每次可选的数为 s u m x \frac{sum}{x} xsum。
由此我们解决了,询问操作,如果是静态问题,可以考虑直接主席树上二分,但是这里是一个带修的问题,所以可以考虑树套树,
如果是树状数组套主席树,空间复杂度将会是 O ( n log 2 n ) O(n \log ^ 2 n) O(nlog2n)的,不可行,所以得通过权值线段树套平衡树来解决,达到空间 O ( n log n ) O(n \log n) O(nlogn)的。
其实还有一个代码量小,同时满足时间复杂度的离线做法,整体二分,空间 O ( n ) O(n) O(n),时间 O ( n log 2 n ) O(n \log ^ 2 n) O(nlog2n)。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, T, cnt, a[N];
long long ans[N];
inline int lowbit(int x) {
return x & (-x);
}
struct BIT {
long long sum[N];
void update(int rt, int x) {
while (rt <= n) {
sum[rt] += x;
rt += lowbit(rt);
}
}
long long query(int rt) {
long long ans = 0;
while (rt) {
ans += sum[rt];
rt -= lowbit(rt);
}
return ans;
}
}sum, num;
struct Res {
int op, id, l, r, k;
/*
op = 0, modify a_l + r
op = 1, query [l, r], k
*/
long long pre_sum, pre_num;
}q[N << 2], ql[N << 2], qr[N << 2];
void solve(int L, int R, long long l, long long r) {
if (L > R) {
return ;
}
if (l == r) {
for (int i = L; i <= R; i++) {
if (!q[i].op) {
ans[q[i].id] = l;
}
}
return ;
}
long long mid = l + r >> 1;
int cnt1 = 0, cnt2 = 0;
for (int i = L; i <= R; i++) {
if (q[i].op) {
if (q[i].r <= mid) {
sum.update(q[i].l, q[i].r * q[i].op), num.update(q[i].l, q[i].op);
ql[++cnt1] = q[i];
}
else {
qr[++cnt2] = q[i];
}
}
else {
long long pre_sum = q[i].pre_sum, pre_num = q[i].pre_num;
long long cur_sum = sum.query(q[i].r) - sum.query(q[i].l - 1), cur_num = num.query(q[i].r) - num.query(q[i].l - 1);
if ((q[i].r - q[i].l + 1) - pre_num - cur_num + (pre_sum + cur_sum) / (mid + 1) >= q[i].k) {
q[i].pre_sum += cur_sum, q[i].pre_num += cur_num;
qr[++cnt2] = q[i];
}
else {
ql[++cnt1] = q[i];
}
}
}
for (int i = 1; i <= cnt1; i++) {
if (ql[i].op) {
sum.update(ql[i].l, -ql[i].r * ql[i].op), num.update(ql[i].l, -ql[i].op);
}
}
for (int i = 1; i <= cnt1; i++) {
q[L + i - 1] = ql[i];
}
for (int i = 1; i <= cnt2; i++) {
q[L + cnt1 + i - 1] = qr[i];
}
solve(L, L + cnt1 - 1, l, mid);
solve(L + cnt1, R, mid + 1, r);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d %d %d", &n, &m, &T);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
a[i] = m - a[i] - 1;
q[++cnt] = {1, 0, i, a[i], 0, 0, 0};
}
for (int i = 1, op; i <= T; i++) {
ans[i] = -1;
scanf("%d", &op);
if (op & 1) {
int l, r, k;
scanf("%d %d %d", &l, &r, &k);
q[++cnt] = {0, i, l, r, r - l + 1 - k, 0, 0};
}
else {
int u, v;
scanf("%d %d", &u, &v);
q[++cnt] = {-1, 0, u, a[u], 0, 0, 0};
a[u] = m - v - 1;
q[++cnt] = {1, 0, u, a[u], 0, 0, 0};
}
}
solve(1, cnt, 0, 100000000000000);
for (int i = 1; i <= T; i++) {
if (ans[i] != -1) {
printf("%lld\n", ans[i]);
}
}
return 0;
}