速度即转发
给定一个长度为 n n n的数组 a a a,里面元素为 a 1 , a 2 , a 3 , … , a n − 1 , a n a_1, a_2, a_3, \dots, a_{n - 1}, a_n a1,a2,a3,…,an−1,an。
有两种操作:
- 修改 a p = k a_p = k ap=k。
- 给定 l , r , k l, r, k l,r,k,设 S ( x ) = ∑ i = l r m a x ( a i − x , 0 ) S(x) = \sum\limits_{i = l} ^{r} max(a_i - x, 0) S(x)=i=l∑rmax(ai−x,0),求 x ∈ [ 0 , 1 0 5 ] x \in[0, 10 ^ 5] x∈[0,105]内满足 S ( x ) ≥ k S(x) \geq k S(x)≥k的最大整数 x x x。
保证任何时刻数组值域在 [ 0 , 1 0 5 ] [0, 10 ^ 5] [0,105],对于查询操作 0 ≤ k ≤ 1 0 5 0 \leq k \leq 10 ^ 5 0≤k≤105。
有个简单的想法树状数组套主席树,对于操作一,直接修改即可 O ( log 2 n ) O(\log ^ 2 n) O(log2n),对于操作二,二分答案 O ( log 3 n ) O(\log ^ 3 n) O(log3n),
显然三个 log \log log的算法,复杂度有点大可能过不了,考虑在线段树上二分答案,
假设我们当前所在的区间是 [ 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],
如果答案在右子树,则答案最少为 m i d + 1 mid + 1 mid+1,这个时候只要判断是否有 l s _ s u m − s z _ l s × ( m i d + 1 ) ≥ k ls\_sum - sz\_ls \times (mid + 1) \geq k ls_sum−sz_ls×(mid+1)≥k即可,
如果成立,则说明答案最少为 m i d + 1 mid + 1 mid+1我们可以进入右子树搜索,否则我们进入右子树搜索,最后我们到达的叶节点即为最优的答案
我们在递归的时候,两个变量 u p p e r _ s u m , u p p e r _ s z upper\_sum, upper\_sz upper_sum,upper_sz,当我们进入左子树的时候,把右子树的 s u m , s z sum, sz sum,sz同时累加到这两个变量上去,
由于我们往左子树走了,说明答案小于
m
i
d
+
1
mid + 1
mid+1了,右子树记录的信息都是
≥
m
i
d
+
1
\geq mid + 1
≥mid+1的
在下一步的
j
u
d
g
e
judge
judge中我们可以直接使用右子树的信息。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, maxn = 100000;
typedef long long ll;
int a[N], n, m;
int root[N], ls[N << 7], rs[N << 7], num;
ll sum[N << 7], sz[N << 7];
inline int lowbit(int x) {
return x & (-x);
}
void update(int &rt, int l, int r, int x, int v) {
if (!rt) {
rt = ++num;
}
sum[rt] += x * v, sz[rt] += v;
if (l == r) {
return ;
}
int mid = l + r >> 1;
if (x <= mid) {
update(ls[rt], l, mid, x, v);
}
else {
update(rs[rt], mid + 1, r, x, v);
}
}
void modify(int rt, int x, int v) {
while (rt <= n) {
update(root[rt], 0, maxn, x, v);
rt += lowbit(rt);
}
}
int A[110], B[110], cnt1, cnt2;
int query(int l, int r, ll upper_sum, ll upper_sz, ll k) {
if (l == r) {
if (upper_sum - upper_sz * l >= k) {
return l;
}
return -1;
}
int mid = l + r >> 1;
ll cur_sum = 0, cur_sz = 0;
for (int i = 1; i <= cnt1; i++) {
cur_sum -= sum[rs[A[i]]], cur_sz -= sz[rs[A[i]]];
}
for (int i = 1; i <= cnt2; i++) {
cur_sum += sum[rs[B[i]]], cur_sz += sz[rs[B[i]]];
}
if (cur_sum + upper_sum - 1ll * (mid + 1) * (upper_sz + cur_sz) >= k) {
for (int i = 1; i <= cnt1; i++) {
A[i] = rs[A[i]];
}
for (int i = 1; i <= cnt2; i++) {
B[i] = rs[B[i]];
}
return query(mid + 1, r, upper_sum, upper_sz, k);
}
else {
for (int i = 1; i <= cnt1; i++) {
A[i] = ls[A[i]];
}
for (int i = 1; i <= cnt2; i++) {
B[i] = ls[B[i]];
}
return query(l, mid, upper_sum + cur_sum, upper_sz + cur_sz, k);
}
}
int get_ans(int l, int r, ll k) {
cnt1 = cnt2 = 0;
for (int i = l - 1; i; i -= lowbit(i)) {
A[++cnt1] = root[i];
}
for (int i = r; i; i -= lowbit(i)) {
B[++cnt2] = root[i];
}
return query(0, maxn, 0, 0, k);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
modify(i, a[i], 1);
}
ll k;
for (int i = 1, op, l, r, p; i <= m; i++) {
scanf("%d", &op);
if (op) {
scanf("%d %lld", &p, &k);
modify(p, a[p], -1);
a[p] = k;
modify(p, a[p], 1);
}
else {
scanf("%d %d %lld", &l, &r, &k);
printf("%d\n", get_ans(l, r, k));
}
}
return 0;
}