4 条题解

  • 0
    @ 2024-10-21 19:48:22

    首先看到题目中要求进行计算前进行区间加操作,不妨考虑线段树。

    对于计算分数的部分,可以将生命值分成如下两部分考虑:

    1. 能够承受完整的若干轮攻击的部分。其中“一轮”表示承受 11nn 怪物的攻击。

    由于怪物每攻击一次攻击力翻倍,那么我们会发现 kk 轮怪物 ii 总共造成的攻击为 $(2^0 + 2^1 + \cdots + 2 ^ {k - 1})\times a_i = 2^k - 1$。
    那么总共能承受的轮数为 log2(mi=1nai+1)\lfloor\log_2(\frac m{\sum_{i=1}^na_i} + 1)\rfloor
    由此可以计算出此部分的答案。

    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 死亡的垃圾桶的攻击)


    时间复杂度 O(qlog2n)O(q\log_2 n),可以通过本题。

    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
    上传者