/*
(如果要加强程序,可以先忽略,将最后一个算好,其他的在二分的时候考虑)
Tips:每一个垃圾桶i强化后(但是不算倍数)的前缀和攻击力为
min((i-l+1)*d,(r-l+1)*d)+ai
将最后一个垃圾桶的攻击力(所有垃圾桶攻击力的总和)一直翻倍
并去减youyou生命值(同时令ans+n)直到无法再承受一整轮攻击为止o(64)
用二分的方法检查youyou的生命值最大大于哪个垃圾桶攻击力前缀和o(logn)
(注意计算翻倍的次数,且用>而不是>=)并直接令ans+下标,输出答案即可。
*/
#include <iostream>
#include <cmath>
using namespace std;
long long a[200005];
long long W, n;
long long beginfix[200005];//上一次维护到第几轮垃圾桶增强
const long long test = 1e18;
struct f {
long long l, r, d;
} addition[1000006];
long long ans = 0;
long long calc(long long pos, long long end) { //计算机器人的战力(不算2^n) 第pos个机器人第end次相加后
a[pos]; //第pos个机器人加强之前的战力
for (long long i = beginfix[pos] + 1; i <= end; i++) {
f add = addition[i];
a[pos] += min((pos - add.l + 1) * add.d, (add.r - add.l + 1) * add.d);
}
beginfix[pos] = end;
return a[pos];
}
long long cpow(long long b) {
long long x = 1;
while (b--)
x *= 2;
return x;
}
long long check(long long w, long long b, long long pos) {
long long r = n, l = 1, mid = (l + r) / 2, tmp = 0; //二分
while (l <= r) {
mid = l + (r - l) / 2;
if (calc(mid, pos) * cpow(b) < w) { //当前机器人计算后(并且乘2^b后不是一击必杀点?)
tmp = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return tmp ; //tmp是一击必杀点,此处只计算到一击必杀点前面
}
int main() {
long long q;//垃圾桶个数,战斗次数,生命值
cin >> n >> q >> W;
for (long long i = 1; i <= n; i++) { //从第一个开始算
cin >> a[i];
a[i] += a[i - 1]; //前缀和
}
for (long long i = 1; i <= q; i++) {
scanf("%lld %lld %lld", &addition[i].l, &addition[i].r, &addition[i].d);
a[n] += (addition[i].r - addition[i].l + 1) * addition[i].d; //最后一个家伙的战力更新
ans = 0;
long long tmp = a[n], b = 0; //最后一项的临时值
long long w = W; //替代品
while (tmp < test && tmp <= w) {
b++;//目前位置翻了多少次方
ans += n;
w -= tmp;
tmp *= 2; //一轮完毕,所有翻倍
}
if (w == 0) {
ans--;
cout << ans << '\n';
} else {
ans += check(w, b, i);
cout << ans << '\n';
}
}
return 0;
}
本人不会线段树,没想到二分也没写出来