返回顶部

LeetCode327. 区间和的个数

327. 区间和的个数

题目描述:给你一个整数数组\(nums\),求它的所有子数组中满足\(lower \leq sum(sub_{nums}) \leq upper\) 的个数。其中\(sub_{nums}\)表示数组\(nums\)的一个子数组,\(sum()\)表示对该数组中的元素求和。

思路:先考虑前缀和,那么一个子数组的和可以表示成\(preSum_{right} - preSum_{left - 1}\),我们顺序枚举,令\(x = preSum_{left - 1}\),那么就是求有个少个\(x\)满足\(lower \leq curSum - x \leq upper\)。转换一下

\[\begin{cases} x \leq up = curSum - lower\\ x \geq low = curSum - upper \end{cases} \]

那么我们只需要通过某种方式求出有多少前缀和满足\(low \leq preSum \leq up\)。因为涉及单点修改和区间查询,所以可以使用树状数组,考虑到这题数据范围很大,所以先预处理所有的\(sum\;up\;low\),然后离散化,在进行答案的求解。

时间复杂度:\(O(nlogn)\)

参考代码

class Solution {
    private int[] tree = new int[300005];
    private int lowbit(int x){return x&-x;}
    private void add(int idx , int n){
        while(idx <= n){
            tree[idx] += 1;
            idx += lowbit(idx);
        }
        return ;
    }
    private int getsum(int idx){
        int res = 0;
        while(idx > 0){
            res += tree[idx];
            idx -= lowbit(idx);
        }
        return res;
    }
    private int getres(int up , int low){
        if(up < low) return 0;
        return getsum(up) - getsum(low - 1);
    }
    public int countRangeSum(int[] nums, int lower, int upper) {
        Set<Long> set = new TreeSet<>();
        set.add(0l);
        int n = nums.length;
        Long sum = 0l;
        for(int i = 0 ; i < n ; ++i){
            sum += nums[i];
            set.add(sum);
            set.add(sum - lower);
            set.add(sum - upper);
        }
        HashMap<Long , Integer> map = new HashMap<>();
        int cur = 0;
        for(Long key : set) map.put(key , ++cur);
        sum = 0l;
        add(map.get(0l) , cur);
        int res = 0;
        for(int i = 0 ; i < n ; ++i){
            sum += nums[i];
            int up = map.get(sum - lower), low = map.get(sum - upper);
            res += getres(up , low);
            add(map.get(sum) , cur);
        }
        return res;
    }
}
posted @ 2021-12-18 22:52  cherish-lgb  阅读(24)  评论(0编辑  收藏  举报