日常訓練2025-1-24

califeee發表於2025-01-24

日常訓練2025-1-24

699. 掉落的方塊

rating:困難

思路(線段樹模擬)

模擬一下這個過程發現,需要維護每個小區間已經有的高度,還要方便的查出小區間的最大值以及整個區間的最大值,所以用線段樹維護。

程式碼

struct Info{
    int val = 0;
};

Info operator+(const Info &a, const Info &b){
    if (a.val > b.val) return a;
    return b;
}

struct SegmentTree {
    int n;
    std::vector<int> tag;
    std::vector<Info> info;
    SegmentTree(int n_) : n(n_), tag(4 * n), info(4 * n) {}

	// 彙總資訊
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    }

	//懶更新
    void add(int p, int v) {
        tag[p] += v;
        info[p].val += v;
    }

	// 把資訊發下去
    void push(int p) {
        add(2 * p, tag[p]);
        add(2 * p + 1, tag[p]);
        tag[p] = 0;
    }
	// 查詢是左閉右開的
    Info query(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return {};
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        push(p);
        return query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y);
    }
    
    Info query(int x, int y) {
        return query(1, 0, n, x, y);
    }
    
    void rangeAdd(int p, int l, int r, int x, int y, int v) {
        if (l >= y || r <= x) {
            return;
        }
        if (l >= x && r <= y) {
            return add(p, v);
        }
        int m = (l + r) / 2;
        push(p);
        rangeAdd(2 * p, l, m, x, y, v);
        rangeAdd(2 * p + 1, m, r, x, y, v);
        pull(p);
    }
    
    // 左閉有開
    void rangeAdd(int x, int y, int v) {
        rangeAdd(1, 0, n, x, y, v);
    }
    
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        push(p);
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    
    void modify(int x, const Info &v) {
        modify(1, 0, n, x, v);
    }
};

class Solution {
public:
    vector<int> fallingSquares(vector<vector<int>>& positions) {
        int n = positions.size();
        std::vector<int> a;
        for (auto e : positions) {
            a.push_back(e[0]);
            a.push_back(e[1]);
        }

        std::sort(a.begin(), a.end());
        a.erase(std::unique(a.begin(), a.end()), a.end());

        int len = a.size();

        std::vector<int> ans;
        SegmentTree stree(len);
        for (int i = 0; i < n; i++){
            int pl = positions[i][0], pr = pl + positions[i][1];
            int l = std::lower_bound(a.begin(), a.end(), pl) - a.begin();
            int r = std::lower_bound(a.begin(), a.end(), pr) - a.begin() - 1;

            int oldval = stree.query(l, r + 1).val;
            int newval = oldval + positions[i][1];
            for (int j = l; j <= r; j++){
                stree.modify(j, {newval});
            }
            ans.push_back(stree.query(0, len).val);
        }


        return ans;
    }
};

E 一起走很長的路!

rating:1700

https://ac.nowcoder.com/acm/contest/95334/E

思路(ST表維護最值)

如果從 1 開始推倒,那麼每個位置之前的重量之和就是字首和,記為 \(f[i-1]\)

如果自身重量大於前面的字首和,那我們就需要進行操作。

那麼我們應該怎麼操作呢?

一個比較顯然的貪心想法是,將第 1 塊多米諾骨牌的重量增加 \(a_i - f[i - 1]\)

那麼對於整個陣列需要給第 1 塊多米諾骨牌增加多少重量呢?顯然是陣列中 \(d_i∈[2,n])\)的最大值。

那麼現在如果是 [l,r]子陣列怎麼辦呢?

在子陣列中應該這樣更新: \(d_i\)

這時我對子陣列進行一次區間加,再查詢最大值就行了,可以直接使用線段樹暴力處理。

當然,我們再稍微思考一下可以發現,根本不需要進行區間加,直接查詢最大值,然後將最大值加上 fl−1f**l−1 即可,可以使用ST表。

注意,最大值要從 l+1 開始取,因為第 1 塊不需要靠前面的推倒。

時間複雜度 $O(nlogn) $。

程式碼

#include<bits/stdc++.h>

using namespace std;

template <typename T>
class ST{
public:
    const int n;
    vector<vector<T>> st;
    ST(int n = 0, vector<T> &a = {}) : n(n){
        st = vector(n + 1, vector<T>(22 + 1));
        build(n, a);
    }

    inline T get(const T &x, const T &y){
        return max(x, y);
    }

    void build(int n, vector<T> &a){
        for(int i = 1; i <= n; i++){
            st[i][0] = a[i];
        }
        for(int j = 1, t = 2; t <= n; j++, t <<= 1){
            for(int i = 1; i <= n; i++){
                if(i + t - 1 > n) break;
                st[i][j] = get(st[i][j - 1], st[i + (t >> 1)][j - 1]);
            }
        }
    }

    inline T find(int l, int r){
        int t = log(r - l + 1) / log(2);
        return get(st[l][t], st[r - (1 << t) + 1][t]);
    }
};

int main(){
    int n, q;
    cin >> n >> q;
    vector f(n + 1, 0ll), d = f;
    for(int i = 1; i <= n; i++){
        int x;
        cin >> x;
        f[i] = f[i - 1] + x;
        d[i] = x - f[i - 1];
    }
    ST<long long> st(n, d);
    while(q--){
        int l, r;
        cin >> l >> r;
        if(l == r){
            cout << 0 << endl;
            continue;
        }
        auto ma = st.find(l + 1, r);
        auto ans = max(ma + f[l - 1], 0ll);
        cout << ans << endl;
    }
}

相關文章