Leetcode 327. 区间和的个数 (前缀和 + 离散化 + 树状数组)
题目
题意
有多少个连续的子数组,其和在
[
l
o
w
e
r
,
u
p
p
e
r
]
[lower, upper]
[lower,upper]之间
题解
可以想到的做法:用前缀和在
O
(
1
)
O(1)
O(1)查询
[
i
,
j
]
[i, j]
[i,j]的和,枚举所有的二元组
[
i
,
j
]
[i, j]
[i,j], 满足条件就加上。
可以优化为:
P
r
e
Pre
Pre为前缀和数组, 从小到大枚举
j
j
j, 由于
lower
≤
P
r
e
[
j
]
−
P
r
e
[
i
−
1
]
≤
upper
\textit{lower} \leq Pre[j] - Pre[i-1] \leq \textit{upper}
lower≤Pre[j]−Pre[i−1]≤upper ,可以得到
P
[
i
−
1
]
P[i-1]
P[i−1] 满足
P
r
e
[
j
]
−
upper
≤
P
r
e
[
i
−
1
]
≤
P
r
e
[
j
]
−
lower
Pre[j] - \textit{upper} \leq Pre[i-1] \leq Pre[j] - \textit{lower}
Pre[j]−upper≤Pre[i−1]≤Pre[j]−lower ,通过枚举
j
j
j,可以将
[
P
r
e
[
j
]
−
upper
,
P
r
e
[
j
]
−
lower
]
[Pre[j] - \textit{upper}, Pre[j] - \textit{lower}]
[Pre[j]−upper,Pre[j]−lower] 看做
[
L
,
R
]
[L, R]
[L,R], 之后查询所有
[
L
,
R
]
[L, R]
[L,R]内的个数即为答案。
使用前缀和算出子数组
[
i
,
j
]
[i, j]
[i,j]的和
P
r
e
[
j
]
−
P
r
e
[
i
]
Pre[j]-Pre[i]
Pre[j]−Pre[i]。
由于数据范围较大,因此可以通过离散化降低数据。我们可以将
P
r
e
[
j
]
−
upper
,
P
r
e
[
j
]
−
lower
,
P
r
e
[
j
]
Pre[j] - \textit{upper}, Pre[j] - \textit{lower}, Pre[j]
Pre[j]−upper,Pre[j]−lower,Pre[j] 一起排序后进行离散化。
这些数据结构都满足在
O
(
l
o
g
n
)
O(logn)
O(logn) 的时间复杂度查询
[
L
,
R
]
[L, R]
[L,R]内的和。
代码
#define ll long long
class Solution {
public:
vector<int> tree;
int n;
int lowbits(int x){
return x & (-x);
}
void update(int x){
while(x <= n){
tree[x] += 1;
x += lowbits(x);
}
}
int query(int x){
int res = 0;
while(x){
res += tree[x];
x -= lowbits(x);
}
return res;
}
int countRangeSum(vector<int>& nums, int lower, int upper) {
ll sums = 0;
vector<ll> preSum = {0};
for(int x : nums){
sums += x;
preSum.emplace_back(sums);
}
set<ll> st;
for(auto x : preSum){
st.insert(x - lower);
st.insert(x);
st.insert(x - upper);
}
unordered_map<ll, int> p;
int c = 0;
for(auto x : st) p[x] = c++;
int res = 0;
n = p.size();
tree = vector<int> (n+5, 0);
for(auto x : preSum){
int left = p[x-upper], right = p[x-lower];
res += query(right+1) - query(left);
update(p[x]+1);
}
return res;
}
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)