B.The Tortoise and the Hare 长春

B. The Tortoise and the Hare

给定一个长度为 n n n的数组 a , ( 1 ≤ a i < m ) a, (1 \leq a_i < m) a,(1ai<m) m m m是一个给定的数 ( 1 ≤ m ≤ 1 0 9 ) (1 \leq m \leq 10 ^ 9) (1m109),有 Q Q Q次操作,分为两类:

  • 给定 u , v , ( 1 ≤ u ≤ n , 1 ≤ v < m ) u, v,(1 \leq u \leq n, 1 \leq v < m) u,v,(1un,1v<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,(1lrn,1krl),每次对 [ l , r ] [l, r] [l,r]区间内前 r − l + 1 − k r - l + 1 - k rl+1k小的数都 + 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 mai1的值,这个问题就变得跟上述问题是一样的了。

考虑如何快速解决这个问题(因为我们不能对每次询问都进行模拟),尝试二分求解:

假设我们当前二分区间为 [ 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 aix的数来说,如果每次删除我们都选他,可以发现对整个过程的模拟是没有影响的,假设这样的数有 c n t cnt cnt个。

接下来我们的问题就转换成为,当所有数都 < x < x <x,我们每次需要挑选 k − c n t k - cnt kcnt个数然后对其 − 1 -1 1,能否进行 x x x次操作了。

我们再对这个问题进行转换,如果我们要进行 x x x次操作,每次我们最多能选多少个数,如果每次可选的数 ≥ k − c n t \geq k - cnt kcnt,则说明合法。

由于 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值