牛客挑战赛48 E 速度即转发 (树套树)

速度即转发

https://ac.nowcoder.com/acm/contest/11161/E

速度即转发

给定一个长度为的数组,里面元素为

有两种操作:

  • 修改
  • 给定,设,求内满足的最大整数

保证任何时刻数组值域在,对于查询操作

有个简单的想法树状数组套主席树,对于操作一,直接修改即可,对于操作二,二分答案

显然三个的算法,复杂度有点大可能过不了,考虑在线段树上二分答案,

假设我们当前所在的区间是,显然左子树代表的值域范围是,右子树所代表的是

如果答案在右子树,则答案最少为,这个时候只要判断是否有即可,

如果成立,则说明答案最少为我们可以进入右子树搜索,否则我们进入右子树搜索,最后我们到达的叶节点即为最优的答案

我们在递归的时候,两个变量,当我们进入左子树的时候,把右子树的同时累加到这两个变量上去,

由于我们往左子树走了,说明答案小于了,右子树记录的信息都是
在下一步的中我们可以直接使用右子树的信息。

#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;
}
全部评论

相关推荐

搜索部&nbsp;首先说下timeline8.18,投递8.19,约一面8.21,晚上一面call约二面8.22,上午二面下午oc周末等待(8.23,8.24)8.25,offer一年前,我还是懵懵懂懂,高考完的暑假,只会提前学学高数,未来的画像是什么?我或许无法预测。开学后,自学Python,接单,无数个客户的ddl,偷偷摸摸一个人找自习的地方,这一步步竟然为后来的我,搭建工程能力的基础。大一上,我也要感谢我的第一位老板,让我接触到了实习,师兄带着我一步步入门,看他们写的飞书文档。大一下,导师带我参与企业项目,这让我渐渐发现,应该去实践,增长见识,而非局限当下,盯着自己的小新pro。不久后,第一波投递开始,结果当然是约面极少。盯着简历上的文字和ssob,我开始思考,确实很多可以去提升。带着些许不甘心,继续沉淀,慢慢的约面也越来越多,有的时候两天7场,准备完就接着下一个日程。这一次,也许是刚好到位吧,比较match,面试答的流利,关关难关关过,成为度孝子展望未来,依然是重重挑战,果然只有收到offer的那一刻是开心的。愿在百度星海拆解的每一段代码,都能成为丈量宇宙的诗行;此志终赴星河,而今迈步重铸天阶。屏幕前的你们,在无数个向星海奔赴的日夜,一定一定,会在未来化作群星回响的征程——请永远相信此刻埋首耕耘的自己!!!
一天三顿半:???百度提前批发 offer了?不是统一和正式批排序完再发吗我靠
百度求职进展汇总
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务