题解:AT_abc467_g [ABC467G] Many Sweets Problem
zhoujunchen · · 题解
题意:从区间
显然贪心优先都选大的。
问题转化为:区间
考虑使用树套树。
外层 BIT 维护位置前缀。
内层权值线段树维护值域上的元素个数以及和。
离散化修改啥的都是板子这里就不写了。
直接写查询。
首先我们要提取区间内对应的线段树的根节点。
lenr = lenl = 0;
for (int i = r; i; i -= i & -i) rlst[lenr++] = root[i];
for (int i = l - 1; i; i -= i & -i) llst[lenl++] = root[i];
ll tot = 0;
然后要判一个无解,就是整段区间内和都小于
然后开始二分加贪心。
优先检查右子树:如果右子树和大于等于
如果不行那么就要把整个右子树都拿走,然后剩余的进入左子树。
如果到达了叶子节点,那么直接计算,需要
不明白的看代码,马蜂比较正常。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 5e5+10, MAXV = 5e5+10, SEGN = 5e7+5;
struct Node {
int lc, rc, cnt;
ll sum;
} tree[SEGN];
int tot;
int n, q, M, V;
ll a[MAXN];
ll vl[MAXV];
int root[MAXN];
int ca[MAXN], xa[MAXN], la[MAXN], ra[MAXN];
ll ka[MAXN];
int rlst[35], llst[35], lenr, lenl, tmpr[35], tmpl[35];
int new_node() {
++tot;
tree[tot] = {0, 0, 0, 0};
return tot;
}
void seg_add(int &rt, int l, int r, int pos, int d, ll val) {
if (!rt) rt = new_node();
tree[rt].cnt += d;
tree[rt].sum += d * val;
if (l == r) return;
int mid = l + r >> 1;
if (pos <= mid)
seg_add(tree[rt].lc, l, mid, pos, d, val);
else
seg_add(tree[rt].rc, mid + 1, r, pos, d, val);
}
void bit_add(int idx, int pos, int d, ll val) {
for (int i = idx; i <= n; i += i & -i)
seg_add(root[i], 1, M, pos, d, val);
}
//以上为树套树板子
int query(int l, int r, ll k) {
lenr = lenl = 0;
for (int i = r; i; i -= i & -i) rlst[lenr++] = root[i];
for (int i = l - 1; i; i -= i & -i) llst[lenl++] = root[i];//提取根编号
ll tot = 0;
for (int i = 0; i < lenr; i++) tot += tree[rlst[i]].sum;
for (int i = 0; i < lenl; i++) tot -= tree[llst[i]].sum;//计算和
if (tot < k) return -1;
int ans = 0, vL = 1, vR = M;
while (vL < vR) {
int mid = (vL + vR) >> 1;
ll rsum = 0;
int rcnt = 0;
for (int i = 0; i < lenr; i++) {
int rc = tree[rlst[i]].rc;
if (rc) rsum += tree[rc].sum, rcnt += tree[rc].cnt;
}
for (int i = 0; i < lenl; i++) {
int rc = tree[llst[i]].rc;
if (rc) rsum -= tree[rc].sum, rcnt -= tree[rc].cnt;
}
if (rsum >= k) {//右子树大于等于 k
int tR = 0, tL = 0;
for (int i = 0; i < lenr; i++) {
int rc = tree[rlst[i]].rc;
if (rc) tmpr[tR++] = rc;
}
for (int i = 0; i < lenl; i++) {
int rc = tree[llst[i]].rc;
if (rc) tmpl[tL++] = rc;
}
copy(tmpr, tmpr + tR, rlst), lenr = tR;
copy(tmpl, tmpl + tL, llst), lenl = tL;
vL = mid + 1;//通过复制线段树根编号进入右子树
} else {//需要左子树
k -= rsum;//注意要减去右子树
ans += rcnt;
int tR = 0, tL = 0;
for (int i = 0; i < lenr; i++) {
int lc = tree[rlst[i]].lc;
if (lc) tmpr[tR++] = lc;
}
for (int i = 0; i < lenl; i++) {
int lc = tree[llst[i]].lc;
if (lc) tmpl[tL++] = lc;
}
copy(tmpr, tmpr + tR, rlst), lenr = tR;
copy(tmpl, tmpl + tL, llst), lenl = tL;
vR = mid;//同右子树
}
}
ll lvl = vl[vL];
ans += (k + lvl - 1) / lvl;//叶子节点
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> q;
V = 0;
for (int i = 1; i <= n; i++) cin >> a[i], vl[++V] = a[i];
for (int i = 1; i <= q; i++) cin >> ca[i] >> xa[i] >> la[i] >> ra[i] >> ka[i], vl[++V] = xa[i];
//离散化&添加节点
sort(vl + 1, vl + V + 1);
M = unique(vl + 1, vl + V + 1) - vl - 1;
for (int i = 1; i <= n; i++) {
int pos = lower_bound(vl + 1, vl + M + 1, a[i]) - vl;
bit_add(i, pos, 1, a[i]);
}
for (int i = 1; i <= q; i++) {
int c = ca[i], x = xa[i], l = la[i], r = ra[i];
ll k = ka[i];
int opos = lower_bound(vl + 1, vl + M + 1, a[c]) - vl;
bit_add(c, opos, -1, a[c]);//删除原来的
a[c] = x;
int npos = lower_bound(vl + 1, vl + M + 1, x) - vl;//添加
bit_add(c, npos, 1, x);
cout << query(l, r, k) << "\n";//查询
}
return 0;
}