题目链接
题目描述
小张记录了连续 天的经营数据
(盈利为正,亏损为负)。给定目标利润区间
,请统计有多少个连续经营周期(即连续子数组)的总利润恰好落在该区间内。
输入:
- 第一行:一个整数
,代表记录的总天数。(
)
- 第二行:
个整数,代表数组
,其中
表示第
天的利润。(
)
- 第三行:两个整数
和
,用空格隔开,代表目标利润区间的左右边界。(
)
输出:
- 一个整数,表示总利润在区间
内的连续经营周期的总数量。
解题思路
本题的核心是统计和在 范围内的子数组数量。
-
前缀和: 定义前缀和
,并令
。 任一连续经营周期
(
)的总利润可以表示为
。 我们需要统计满足
的索引对
。
-
算法选择:
- 暴力枚举 (
):枚举所有子数组起点和终点。对于
,子数组数量级为
。在 C++ 和 Java 中,这种复杂度通常可以在 1 秒内通过;但在 Python 中会超时。
- 前缀和 + 树状数组 (
):将不等式变形为
。在遍历
的过程中,利用树状数组统计满足条件的
。这种方法对
非常轻松,且适用于更大的
。
- 暴力枚举 (
-
优化与细节:
- 考虑到
的数据范围,C++ 使用
更加简洁。
- Python 必须使用优化算法(如树状数组或归并排序思想)才能通过。为了保证通用性和专业性,本题解采用
的树状数组方案。
- 由于前缀和的值域范围在
,我们需要对前缀和进行离散化处理。
- 考虑到
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
struct FenwickTree {
int n;
vector<int> tree;
FenwickTree(int size) : n(size), tree(size + 1, 0) {}
void update(int i, int delta) {
for (; i <= n; i += i & -i) tree[i] += delta;
}
int query(int i) {
int res = 0;
for (; i > 0; i -= i & -i) res += tree[i];
return res;
}
int query(int l, int r) {
return l > r ? 0 : query(r) - query(l - 1);
}
};
int main() {
int n;
cin >> n;
vector<LL> s(n + 1, 0);
vector<LL> all_sums;
all_sums.push_back(0);
for (int i = 1; i <= n; ++i) {
LL p;
cin >> p;
s[i] = s[i - 1] + p;
all_sums.push_back(s[i]);
}
LL L, R;
cin >> L >> R;
sort(all_sums.begin(), all_sums.end());
all_sums.erase(unique(all_sums.begin(), all_sums.end()), all_sums.end());
auto getIdx = [&](LL val) {
return lower_bound(all_sums.begin(), all_sums.end(), val) - all_sums.begin() + 1;
};
FenwickTree ft(all_sums.size());
LL ans = 0;
for (int j = 0; j <= n; ++j) {
LL val_low = s[j] - R;
LL val_high = s[j] - L;
int idx_l = lower_bound(all_sums.begin(), all_sums.end(), val_low) - all_sums.begin() + 1;
int idx_r = upper_bound(all_sums.begin(), all_sums.end(), val_high) - all_sums.begin();
ans += ft.query(idx_l, idx_r);
ft.update(getIdx(s[j]), 1);
}
cout << ans << endl;
return 0;
}
import java.util.*;
public class Main {
static class FenwickTree {
int n;
int[] tree;
FenwickTree(int size) {
this.n = size;
this.tree = new int[size + 1];
}
void update(int i, int delta) {
for (; i <= n; i += i & -i) tree[i] += delta;
}
int query(int i) {
int res = 0;
for (; i > 0; i -= i & -i) res += tree[i];
return res;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long[] s = new long[n + 1];
List<Long> allSums = new ArrayList<>();
allSums.add(0L);
for (int i = 1; i <= n; i++) {
s[i] = s[i - 1] + sc.nextLong();
allSums.add(s[i]);
}
long L = sc.nextLong();
long R = sc.nextLong();
Collections.sort(allSums);
List<Long> uniqueSums = new ArrayList<>();
if (!allSums.isEmpty()) {
uniqueSums.add(allSums.get(0));
for (int i = 1; i < allSums.size(); i++) {
if (!allSums.get(i).equals(allSums.get(i - 1))) {
uniqueSums.add(allSums.get(i));
}
}
}
FenwickTree ft = new FenwickTree(uniqueSums.size());
long ans = 0;
for (int j = 0; j <= n; j++) {
long valLow = s[j] - R;
long valHigh = s[j] - L;
int idxL = Collections.binarySearch(uniqueSums, valLow);
if (idxL < 0) idxL = -(idxL + 1);
int idxR = Collections.binarySearch(uniqueSums, valHigh);
if (idxR < 0) idxR = -(idxR + 1) - 1;
ans += ft.query(idxR + 1) - ft.query(idxL);
int curIdx = Collections.binarySearch(uniqueSums, s[j]);
ft.update(curIdx + 1, 1);
}
System.out.println(ans);
}
}
import bisect
def solve():
n = int(input())
p_list = list(map(int, input().split()))
l_bound, r_bound = map(int, input().split())
s = [0] * (n + 1)
for i in range(n):
s[i + 1] = s[i] + p_list[i]
# 离散化
all_sums = sorted(list(set(s)))
tree_size = len(all_sums)
bit = [0] * (tree_size + 1)
def update(i, delta):
while i <= tree_size:
bit[i] += delta
i += i & (-i)
def query(i):
res = 0
while i > 0:
res += bit[i]
i -= i & (-i)
return res
ans = 0
for val_s in s:
val_low = val_s - r_bound
val_high = val_s - l_bound
idx_l = bisect.bisect_left(all_sums, val_low) + 1
idx_r = bisect.bisect_right(all_sums, val_high)
ans += query(idx_r) - query(idx_l - 1)
cur_idx = bisect.bisect_left(all_sums, val_s) + 1
update(cur_idx, 1)
print(ans)
if __name__ == "__main__":
solve()
算法及复杂度
- 算法:前缀和 + 离散化 + 树状数组。
- 时间复杂度:
。计算前缀和为
,排序离散化为
,树状数组操作为
。针对
的规模,该算法非常高效。
- 空间复杂度:
。用于存储前缀和、离散化数组及树状数组。

京公网安备 11010502036488号