2023-07-26 Leetcode 2569 更新数组后处理求和查询

192 阅读1分钟

Problem: 2569. 更新数组后处理求和查询

思路

设计对数组的一个部分进行统一修改,必然是线段树,不然一次修改的耗时为O(n)

解题方法

先构建一个线段树,然后每次修改进行懒标记即可

Code


class Solution {
    static constexpr int MX = 4e5 + 1;

    int cnt1[MX]; // 统计1的个数
    bool flip[MX]; // 懒标记, 标记是否需要反转

    // 维护区间中1的个数
    void maintain(int o) {
        // 1的个数等于其两个子区间的1的个数之和
        cnt1[o] = cnt1[o * 2] + cnt1[o * 2 + 1];
    }

    // 区间反转 对第o个特殊区间,即[l, r] 进行反转
    void do_(int o, int l, int r) {
        // 1的个数为 区间长度 - 原有的1的个数
        cnt1[o] = r - l + 1 - cnt1[o];
        // flip用于标记整个区间是否需要反转(懒标记)
        flip[o] = !flip[o];
    }

    // 构造特殊区间
    void build(vector<int> &a, int o, int l, int r) {
        if (l == r) {
            cnt1[o] = a[l - 1];
            return;
        }
        int m = (l + r) / 2;
        build(a, o * 2, l, m); // 耗时log(n)
        build(a, o * 2 + 1, m + 1, r); // 耗时log(n)
        maintain(o);
    }


    // 反转区间[L, R], 目前的特殊区间为[l, r]
    void update(int o, int l, int r, int L, int R) {
        if (L <= l && r <= R) {
            // 直接update整个区间 耗时log(n)
            do_(o, l, r);
            return;
        }
        // 取[l, r]中点
        int m = (l + r) / 2;

        // 如果标记了需要反转
        if (flip[o]) {
            // 执行区间反转,但只向下一层 耗时o(1)
            do_(o * 2, l, m);
            do_(o * 2 + 1, m + 1, r);
            // 该层的懒标记消除
            flip[o] = false;
        }
        // 如果中点在L右侧,那么对[l, m]的特殊区间进行更新 耗时log(n)
        // 如果中点在R左侧,那么对[m + 1, R]的特殊区间进行更新 耗时log(n)
        if (m >= L) update(o * 2, l, m, L, R);
        if (m < R) update(o * 2 + 1, m + 1, r, L, R);
        // 重新统计1的个数,耗时log(n)
        maintain(o);
    }
public:
    vector<long long> handleQuery(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
        int n = nums1.size();
        build(nums1, 1, 1, n);
        vector<long long> ret;

        long long sum = accumulate(nums2.begin(), nums2.end(), 0LL);
        for (auto &q : queries) {
            if (q[0] == 1) update(1, 1, n, q[1] + 1, q[2] + 1);
            else if(q[0] == 2) sum += 1LL * q[1] * cnt1[1];
            else ret.push_back(sum);
        }

        return ret;
    }
};