五月集训(第25天)—树状数组

树状数组

知识点

每个结点管辖的区域范围是 [x - lowbit(x) + 1, x]

1. 327. 区间和的个数

这题说的晦涩难懂,先解释一下题意:

给定一个整数数组 nums和范围区间[lower,upper],已知数组nums内的元素个数为size,以元素索引(下标)为起点,元素个数逐次递增的方式迭代返回数组内的元素和,当元素和落在范围区间[lower,upper]内时,返回元素索引区间S(i,j),如此反复直至元素索引i=size,求索引区间S(i,j)的个数.
例子:
输入: nums = [-2,5,-1], lower = -2, upper = 2,
输出: 3
解释: 元素索引 i=0时,元素个数逐次递增迭代有以下子数组[-2,],[-2,5],[-2,5,-1],对应元素和为-2,3,2,其中-2和2落在范围区间[lower = -2, upper = 2]之间,因此元素索引区间[0,0],[0,2]符合要求(注意是元素索引区间)
以此类推i=1时有子数组[5],[5,-1],
i=2时有子数组[-1]
,最终得到符合要求的元素索引区间为[0,0],[0,2],[2,2].

    思路:
         迷迷糊糊看了个大概,还是得看英雄哥

class Solution {
    #define ll long long
    #define N  100010
    ll s[N];
    vector<ll> bin;

    int get_index(ll val) {
        int l = 0, r = bin.size() - 1;
        while (l <= r) {
            int mid = l + ((r - l) >> 1);
            if (bin[mid] == val) return mid + 1;
            else if (val > bin[mid]) l = mid + 1;
            else r = mid - 1;
        }
        return -1;
    }

    int c[3 * N];   // 增量标记
    int lowbit(int x) {
        return x & -x;
    }

    void add(int x, int n) {    /* 向树状数组的区间增加值 */
        while (x <= n) {
            c[x]++;
            x += lowbit(x);
        }
    }

    ll sum(int x) {     /* 询问 sum(x) 前缀和 */
        ll s = 0;
        while (x) {
            s += c[x];
            x -= lowbit(x);
        }
        return s;
    }

public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int i, nums_size = nums.size();
        int ans = 0;
        bin.clear();
        memset(c, 0, sizeof(c));

        // 求前缀和
        s[0] = 0;
        for (i = 1; i <= nums_size; ++i) {  
            s[i] = s[i - 1] + nums[i - 1];
        }


        // 将   lower <= s[i] - s[k] <= upper 转化为求 s[i] - upper <= s[k] <= s[i] - lower 的个数
        for (i = 0; i <= nums_size; ++i) {
            bin.push_back( s[i] );
            bin.push_back( s[i] - upper );
            bin.push_back( s[i] - lower );
        }
        sort(bin.begin(), bin.end());
        bin.erase( unique(bin.begin(), bin.end()), bin.end() ); // 离散化
        
        add( get_index( s[0] ), bin.size() );
        for (i = 1; i <= nums_size; ++i) {
            ans += sum( get_index( s[i] - lower ) ) - sum( get_index( s[i] - upper ) - 1 ); /* 获取 s[i] - lower 到 s[i] - upper ) 之间的前缀和(即满足条件的前缀和)  */
            add( get_index( s[i] ), bin.size() );
        }
        return ans;
    }
};

posted @ 2022-06-01 17:48  番茄元  阅读(25)  评论(0)    收藏  举报