救命!有懒标记,而且疯狂卡常,但是一直TLE!

P3372 【模板】线段树 1

Keyal @ 2024-01-09 00:33:08

这里学的时候写的注释是“延迟更新”,含义和“懒标记”相同。 一直卡在70分,最后三个全部TLE。

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class SegmentTree {

private:

    vector<long long> tree;
    vector<long long> lazy;
    size_t n;

    inline void buildTree(const vector<int> &data, int treeIndex, int l, int r) {
        if (l == r) {
            tree[treeIndex] = data[l];
            return;
        }
        int mid = l + ((r - l) >> 1);
        long long dou_ti = treeIndex << 1;
        buildTree(data, dou_ti + 1, l, mid);
        buildTree(data, dou_ti + 2, mid + 1, r);
        // 在后序递归位置,逐步将各个小区间和合并成大区间。
        tree[treeIndex] = tree[dou_ti + 1] + tree[dou_ti + 2];
    }

    inline long long queryRange(int treeIndex, int sl, int sr, int l, int r) {
        if (sl == l && sr == r) {
            return tree[treeIndex];
        }
        // 每次访问一个节点都要检查其延迟更新的状态,否则会导致没有完成更新的错误。
        pushDown(treeIndex, sl, sr);
        int mid = sl + ((sr - sl) >> 1);
        int dou_ti = treeIndex << 1;
        if (r <= mid) {
            // 查询的区间完全在左子区间中
            return queryRange(dou_ti + 1, sl, mid, l, r);
        } else if (l > mid) {
            // 查询的区间完全在右子区间中
            return queryRange(dou_ti + 2, mid + 1, sr, l, r);
        } else {
            // 查询的区间与左右子区间交错
            return queryRange(dou_ti + 1, sl, mid, l, mid)
                   +
                   queryRange(dou_ti + 2, mid + 1, sr, mid + 1, r);
        }
    }

    inline void pushDown(int treeIndex, int l, int r) {
        if (lazy[treeIndex] != 0) {
            int mid = l + ((r - l) >> 1);
            long long dou_ti = treeIndex << 1;
            // 将当前节点的延迟更新应用到左右子节点
            // 解释:
            // [l, mid]一共有mid - l + 1个节点,如果给[l, r]全部增加lazy[treeIndex],
            // 那么[l, mid]所代表的节点的区间和应该增加lazy[treeIndex] * (mid - l + 1)
            // [mid + 1, r]同理
            tree[dou_ti + 1] += lazy[treeIndex] * (mid - l + 1);
            tree[dou_ti + 2] += lazy[treeIndex] * (r - mid);
            // 更新左右子节点的延迟更新值,以便记录它们的子节点的未更新的值
            lazy[dou_ti + 1] += lazy[treeIndex];
            lazy[dou_ti + 2] += lazy[treeIndex];
            // 当前节点的延迟更新已经应用完毕,因此要清除。
            lazy[treeIndex] = 0;
        }
    }

    inline void updateRange(int treeIndex, int sl, int sr, int l, int r, long long val) {
        if (sl == r && sr == r) {
            tree[treeIndex] += val * (r - l + 1);
            lazy[treeIndex] += val;
            return ;
        }
        // 只要访问到了就一定要检查延迟更新的状态
        pushDown(treeIndex, sl, sr);
        int mid = sl + ((sr - sl) >> 1);
        int dou_ti = treeIndex << 1;

        // 这里和区间查询的逻辑差不多,区别只有如下两个
        // 多带了一个参数"val",
        // 在后序递归位置进行的更新操作(其实和buildTree时的操作差不多)。
        if (r <= mid) {
            updateRange(dou_ti + 1, sl, mid, l, r, val);
        } else if (l > mid) {
            updateRange(dou_ti + 2, mid + 1, sr, l, r, val);
        } else {
            updateRange(dou_ti + 1, sl, mid, l, mid, val),
            updateRange(dou_ti + 2, mid + 1, sr, mid + 1, r, val);
        }

        tree[treeIndex] = tree[dou_ti + 1] + tree[dou_ti + 2];

    }

public:
    explicit SegmentTree(const vector<int> &data) {
        n = data.size();
        size_t fn = 4 * n;
        tree.resize(fn);
        lazy.resize(fn, 0);
        buildTree(data, 0, 0, n - 1);
    }

    inline long long query(int l, int r) {
        return queryRange(0, 0, n - 1, l, r);
    }

    inline void update(int l, int r, long long val) {
        updateRange(0, 0, n - 1, l, r, val);
    }

};
int read()
{
    int c = getchar(), f = 1, res = 0;
    while (c > '9' || c < '0') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c <= '9' && c >= '0') res = (res << 1) + (res << 3) + (c - '0'), c = getchar();
    return res;
}
int main() {
//    ios::sync_with_stdio(false);
//    cin.tie(0);
    int n = read(), m = read();
    vector<int> data(n);
    for (int i = 0; i < n; ++i) cin >> data[i];

    SegmentTree st(data);

    while (m--) {
        int type = read(), x = read(), y = read();
        long long k;
        x--, y--;
        if (type == 1) {
            k = read();
            st.update(x, y, k);
        } else if (type == 2) {
            printf("%lld\n", st.query(x, y));
        } else {
            throw "ERROR TYPE" + to_string(type);
        }
    }

}

by hegm @ 2024-01-09 06:39:42

@Keyal 70分那你就是复杂度错了呗,我觉得不可能是常熟问题吧。


by Fractured_Angel @ 2024-01-09 06:57:25

你这线段树看着有点离谱啊(


by Keyal @ 2024-01-09 08:50:57

啊?这样说起来这个线段树是有问题的?我去wiki看看......


by sssscy_free_stdio @ 2024-01-09 09:12:27

@Keyal 这线段树好像真的不太正常,70分不就是过了 70\% 的数据吗,复杂度肯定不对啊


by Keyal @ 2024-01-09 09:14:44

我懂了!我懂了!复杂度没有问题,有问题的是 updateRange 方法里面的if判断,我写成了if (sl == r && sr == r) ,这里应该是 if (sl == l && sr == r)。

感谢大佬们的帮助


by sssscy_free_stdio @ 2024-01-09 09:15:44

@Keyal 啊啊啊我没仔细看,wssb


|