牛客挑战赛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; }