五月集训(第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;
}
};

东方欲晓,莫道君行早。

浙公网安备 33010602011771号