4 条题解
-
0
首先看到题目中要求进行计算前进行区间加操作,不妨考虑线段树。
对于计算分数的部分,可以将生命值分成如下两部分考虑:
- 能够承受完整的若干轮攻击的部分。其中“一轮”表示承受 至 怪物的攻击。
由于怪物每攻击一次攻击力翻倍,那么我们会发现 轮怪物 总共造成的攻击为 $(2^0 + 2^1 + \cdots + 2 ^ {k - 1})\times a_i = 2^k - 1$。
那么总共能承受的轮数为 。
由此可以计算出此部分的答案。- 剩余的不完整部分。
很容易想到二分,但是直接二分并不好写。注意到线段树本身有二分的性质,其实已经完美解决了直接二分的问题。 这里可以把线段树当作与二叉搜索树来处理。从根节点出发往下搜,如果值比左儿子小那就走左边,否则走右边,如果走到叶节点就返回。当然也可以当值等于左儿子的值时返回,不过本蒟蒻这里并没有实现这个方案。
核心代码如下:
// k表示这一轮怪物攻击力翻了多少倍 // _rest表示剩余生命值 ll find(ll k, ll _rest, ll p = 1) { if (t[p].l == t[p].r) return t[p].l; pushdown(p); ll mid = t[p].l + t[p].r >> 1; assert(_rest < t[p].val * k); if (_rest <= t[p << 1].val * k) return find(k, _rest, p << 1); else return find(k, _rest - t[p << 1].val * k, p << 1 | 1); }
最后记得答案减一。(战斗分数的计算不包括恰好使 youyou 死亡的垃圾桶的攻击)
时间复杂度 ,可以通过本题。
code:
#include<bits/stdc++.h> #define endl '\n' using namespace std; using ll = long long; const ll N = 2e5 + 5; ll n, q, m, a[N]; // l, r:区间左右端点 // val:线段树节点的值 // add:懒标记 struct tree { ll l, r; ll val, add; } t[N << 2]; // p:当前线段树节点 // p << 1:左儿子 // p << 1 | 1:右儿子 void build(ll l, ll r, ll p = 1) { t[p].l = l; t[p].r = r; if (l == r) { t[p].val = a[l]; return; } ll mid = l + r >> 1; build(l, mid, p << 1); build(mid + 1, r, p << 1 | 1); t[p].val = t[p << 1].val + t[p << 1 | 1].val; } void pushdown(ll p) { if (t[p].add) { t[p << 1].val += t[p].add * (t[p << 1].r - t[p << 1].l + 1); t[p << 1 | 1].val += t[p].add * (t[p << 1 | 1].r - t[p << 1 | 1].l + 1); t[p << 1].add += t[p].add; t[p << 1 | 1].add += t[p].add; t[p].add = 0; } } void update(ll l, ll r, ll z, ll p = 1) { if (l <= t[p].l && r >= t[p].r) { t[p].val += z * (t[p].r - t[p].l + 1); t[p].add += z; return; } pushdown(p); ll mid = t[p].l + t[p].r >> 1; if (l <= mid) update(l, r, z, p << 1); if (r > mid) update(l, r, z, p << 1 | 1); t[p].val = t[p << 1].val + t[p << 1 | 1].val; } ll query(ll l, ll r, ll p = 1) { if (l <= t[p].l && r >= t[p].r) return t[p].val; pushdown(p); ll mid = t[p].l + t[p].r >> 1, res = 0; if (l <= mid) res += query(l, r, p << 1); if (r > mid) res += query(l, r, p << 1 | 1); return res; } // 这里的k表示这一轮怪物攻击力翻了多少倍 ll find(ll k, ll _rest, ll p = 1) { if (t[p].l == t[p].r) return t[p].l; pushdown(p); ll mid = t[p].l + t[p].r >> 1; // assert(_rest < t[p].val * k); if (_rest <= t[p << 1].val * k) return find(k, _rest, p << 1); else return find(k, _rest - t[p << 1].val * k, p << 1 | 1); } void solve() { cin >> n >> q >> m; for (ll i = 1; i <= n; i++) cin >> a[i]; build(1, n); while (q--) { ll l, r, k, _rest, ans; cin >> l >> r >> k; update(l, r, k); // k:轮数 k = __lg(m / query(1, n) + 1); ans = k * n; // _rest:不满一轮的剩余生命值 _rest = m - query(1, n) * ((1ll << k) - 1); if (_rest) ans += find(1ll << k, _rest); // cout << ans - 1 << endl; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int _ = 1; // cin >> _; while (_--) solve(); return 0; }
信息
- ID
- 85
- 时间
- 1500ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 701
- 已通过
- 164
- 上传者