【Leetcode327】区间和的个数

题目描述

给你一个整数数组 \(nums\) 以及两个整数 \(lower\)\(upper\) 。求数组中,值位于范围 \([lower, upper]\) (包含 \(lower\) 和 \(upper\))之内的区间和的个数 。

区间和 \(S(i, j)\) 表示在 \(nums\) 中,位置从 \(i\) 到 \(j\) 的元素之和,包含 \(i\) 和 \(j\) \((i \le j)\)

分析

\(O(n^2)\)做法

由区间和很容易想到前缀和做法,于是处理出前缀和数组sum[i],枚举区间的开头和结尾进行判断即可。

\(O(n\log n)\)做法

只枚举区间的结尾,查询以此为结尾的区间中有多少个符合条件的区间。

由前缀和公式得 \(lower \le sum[end] - sum[start] \le upper\) ,则有 \(sum[end]-upper \le sum[start] \le sum[end] - lower\) ,即对sum[start]进行区间查询,得到满足条件的start个数有多少个。

用权值线段树来维护 \(sum[start]\) ,并且由于该题 \(nums[i]\) 数据范围较大,但 \(nums\) 数组个数较少,可以使用离散化方法对其进行哈希处理。哈希过程中使用 upper_bound 函数,需要注意查询上界的处理。

class Solution {
public:
    int tree[400010]={0},l_of_nums=0,k;
    long long sum[100005]={0},b[100005]={0};
    int hash(long long number){
        return lower_bound(sum,sum+k,number)-sum;
    }
    void change(int from,int to,int cur_node,int tar_index){//对 tar_index 位置+1
        //from-to:当前节点值代表这个区间范围的和
        //cur_node:当前节点在树中的标号
        //tar_index:要修改的区间点值
        if(from==to){
            tree[cur_node]++;
            return;
        }
        int mid=(from+to)>>1;
        if(mid<tar_index)change(mid+1,to,cur_node<<1|1,tar_index);
        else change(from,mid,cur_node<<1,tar_index);
        tree[cur_node]=tree[cur_node<<1]+tree[cur_node<<1|1];
    }
    int query(int from,int to,int cur_node,int lower,int upper){
        if(from>=lower && to<=upper)return tree[cur_node];
        int query_ans=0,mid=(from+to)>>1;
        if(lower<=mid)query_ans+=query(from,mid,cur_node<<1,lower,upper);
        if(upper>mid)query_ans+=query(mid+1,to,cur_node<<1|1,lower,upper);
        return query_ans;
    }
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        l_of_nums=nums.size();
        sum[0]=0;
        for(int i=0;i<l_of_nums;i++)
        {
            sum[i+1]=sum[i]+nums[i];
            b[i]=sum[i+1];
        }      //前缀和
        k=l_of_nums+1;
        sum[k++]=-1e15;
        sum[k++]=1e15;
        sort(sum,sum+k);  //sum[i]排序 离散化
        k=unique(sum,sum+k)-sum; //得到有k个不同的sum[i]数值 去重[0,k-1]
        int ans=0;
        change(0,k-1,1,hash(0));
        for(int end=0;end<l_of_nums;end++) //枚举b[end]
        {
            int l=hash(b[end]-upper);
            int r=hash(b[end]-lower);//>=
            if (sum[r]>b[end]-lower) r--;
            int cnt=query(0,k-1,1,l,r);
            //cout<<end<<" "<<b[end]<<" "<<cnt<<endl;
            ans+=cnt; //cnt:使得lower<sum[end]-sum[start]<upper的start个数,start<end,即 sum[end]-upper<sum[start]<sum[end]-lower 
            change(0,k-1,1,hash(b[end]));
        }
        return ans;
    }
};
\```
posted @ 2021-09-09 21:23  Tsyxxxka  阅读(48)  评论(0)    收藏  举报
levels of contents