题解:AT_abc467_g [ABC467G] Many Sweets Problem
树套树做法太慢了,这里给出一种整体二分的做法。
因为带修,所以最好不要排序。我们尝试对每个询问在线地进行二分:我们二分最大的阈值
我们都知道二分是一种分治,我们可以将它看成在线段树上遍历一条由根到叶子的路径。如果直接在线进行二分的话,有很多点会被重复遍历。一个点被计算的这好几次,其实可以被合并到一次进行计算的。
具体地,我们将每一个值的出现和消失看作单点
我们递归地解决这个问题:
我们依次遍历这些事件,并使用一个轻量的 BIT 维护目前序列中所有处于
-
如果我们遇到修改操作,若它的绝对值是在
(mid,r] 中,那么我们在 BIT 上进行对应的操作,把它放到E_r 里;否则直接放入E_l 。 -
遇到查询,我们直接查询该区间和
S ,S 的意义上面已经说了。如果S \ge k_i ,那么说明对于这个询问,答案在(mid,r] 之间,放入E_r ;否则,答案在[l,mid] 之间。因为我们不会把(mid,r] 里的操作放入E_l ,因此,我们先在k_i 上把这些操作的贡献减去,即k_i \leftarrow k_i - S ,然后把该操作放入E_l 。
接着递归
对于最终答案的统计,我们另开一个 BIT 维护目前有多少个
结束条件就是
我们发现,树套树的两个维度分别对应了权值和区间。整体二分,不像树套树对第二维(区间)进行维护,而是直接实时计算,把所有调用通过一次计算搞定。所以空间小了,但是失去了在线功能。因为每个操作在每一层被处理了一次,时间为
// Problem: G - Many Sweets Problem
// Contest: AtCoder - AtCoder Beginner Contest 467
// Memory Limit: 1024 MB
// Time Limit: 3000 ms
#include <bits/stdc++.h>
#define int long long
#define Misaka namespace
#define Network std
using Misaka Network;
/*
if(s[i].id == 31)
if(l == 0 && r == 250000000)
*/
const int N = 1e5 + 7;
int n, q, a[N], m;
struct Event{
int opt, id, ans;
int c, x;
int l, r, k;
} s[N << 2], t[N << 2];
int to[N << 2];
struct BIT{
int val[N];
#define lb(x) (x & (-x))
stack<tuple<int, int>> q;
void add(int x, int k){
for(int i = x; i <= n; i += lb(i)) val[i] += k;
q.push({x, k});
}
int qur(int x){
int res = 0;
for(int i = x; i; i -= lb(i)) res += val[i];
return res;
}
int sum(int l, int r){
return qur(r) - qur(l - 1);
}
void init(){
while(q.size()){
auto [x, vv] = q.top(); q.pop();
for(int i = x; i <= n; i += lb(i)) val[i] -= vv;
}
}
} bit, cnt;
void solve(int l, int r, int ql, int qr){
if(l == r){
for(int i = ql; i <= qr; i ++){
if(l == 0) s[i].ans = -1;
else s[i].ans += s[i].k / l + (s[i].k % l > 0);
}
return;
}
int mid = (l + r) >> 1;
bit.init();
cnt.init();
int cl = 0, cr = 0;
for(int i = ql; i <= qr; i ++){
if(s[i].opt == 1){
if(abs(s[i].x) > mid){
bit.add(s[i].c, s[i].x), cr ++, to[i] = 2;
cnt.add(s[i].c, s[i].x / abs(s[i].x));
}
else cl ++, to[i] = 1;
}
else{
int S = bit.sum(s[i].l, s[i].r);
if(S >= s[i].k) cr ++, to[i] = 2;
else cl ++, to[i] = 1, s[i].k -= S, s[i].ans += cnt.sum(s[i].l, s[i].r);
}
}
int u = ql, v = ql + cl;
for(int i = ql; i <= qr; i ++){
if(to[i] == 1) t[u ++] = s[i];
else t[v ++] = s[i];
}
for(int i = ql; i <= qr; i ++) s[i] = t[i];
if(cl) solve(l, mid, ql, ql + cl - 1);
if(cr) solve(mid + 1, r, ql + cl, qr);
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; i ++){
cin >> a[i]; m ++;
s[m] = {1, m, 0, i, a[i], 0, 0, 0};
}
for(int i = 1; i <= q; i ++){
int c, x, l, r, k;
cin >> c >> x >> l >> r >> k;
m ++; s[m] = {1, m, 0, c, -a[c], 0, 0, 0};
a[c] = x;
m ++; s[m] = {1, m, 0, c, a[c], 0, 0, 0};
m ++; s[m] = {2, m, 0, 0, 0, l, r, k};
}
solve(0, 1e9, 1, m);
sort(s + 1, s + m + 1, [](Event a, Event b){
return a.id < b.id;
});
for(int i = 1; i <= m; i ++){
if(s[i].opt == 2) cout << s[i].ans << "\n";
}
return 0;
}