小店的经营分析
题目分析
给定 天的经营数据(盈利为正,亏损为负),以及一个目标利润区间
,求有多少个连续经营周期(子数组)的总利润恰好落在目标区间内。
即统计满足 的
对数。
思路
归并排序 + 前缀和
子数组和可以用前缀和表示:。问题转化为:在前缀和数组
中,统计满足
(且
)的有序对
的个数。
暴力枚举所有对是 的,对于
会超时。我们利用归并排序在合并过程中高效统计。
关键观察:在归并排序的合并阶段,左半部分和右半部分各自已排好序。对于左半部分的每个 ,我们要在右半部分找满足
的
,即
。
由于右半部分已排序,可以用双指针 维护满足条件的区间
。随着
从左到右遍历(左半部分也已排序,
递增),
和
也单调递增,因此统计部分的总复杂度为
。
以样例 2 验证: ,
,
。
所有子数组和:。落在
内的有:
,共
个,与预期一致。
复杂度
- 时间复杂度:
,归并排序每层合并
,共
层
- 空间复杂度:
,归并排序的辅助数组
代码
#include <bits/stdc++.h>
using namespace std;
int countRangeSum(vector<long long>& prefix, int lo, int hi, long long lower, long long upper) {
if (lo >= hi) return 0;
int mid = lo + (hi - lo) / 2;
int count = countRangeSum(prefix, lo, mid, lower, upper)
+ countRangeSum(prefix, mid + 1, hi, lower, upper);
// 对于左半部分的每个 prefix[i],统计右半部分中满足条件的 j
int j1 = mid + 1, j2 = mid + 1;
for (int i = lo; i <= mid; i++) {
while (j1 <= hi && prefix[j1] - prefix[i] < lower) j1++;
while (j2 <= hi && prefix[j2] - prefix[i] <= upper) j2++;
count += j2 - j1;
}
// 归并排序
vector<long long> tmp(hi - lo + 1);
int p = lo, q = mid + 1, idx = 0;
while (p <= mid && q <= hi) {
if (prefix[p] <= prefix[q]) tmp[idx++] = prefix[p++];
else tmp[idx++] = prefix[q++];
}
while (p <= mid) tmp[idx++] = prefix[p++];
while (q <= hi) tmp[idx++] = prefix[q++];
for (int i = 0; i < idx; i++) prefix[lo + i] = tmp[i];
return count;
}
int main() {
int n;
scanf("%d", &n);
vector<long long> prefix(n + 1);
prefix[0] = 0;
for (int i = 1; i <= n; i++) {
long long x;
scanf("%lld", &x);
prefix[i] = prefix[i - 1] + x;
}
long long lower, upper;
scanf("%lld %lld", &lower, &upper);
printf("%d\n", countRangeSum(prefix, 0, n, lower, upper));
return 0;
}
import java.util.*;
public class Main {
static long lower, upper;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long[] prefix = new long[n + 1];
for (int i = 1; i <= n; i++) {
prefix[i] = prefix[i - 1] + sc.nextLong();
}
lower = sc.nextLong();
upper = sc.nextLong();
System.out.println(mergeCount(prefix, 0, n));
}
static int mergeCount(long[] arr, int lo, int hi) {
if (lo >= hi) return 0;
int mid = lo + (hi - lo) / 2;
int count = mergeCount(arr, lo, mid) + mergeCount(arr, mid + 1, hi);
int j1 = mid + 1, j2 = mid + 1;
for (int i = lo; i <= mid; i++) {
while (j1 <= hi && arr[j1] - arr[i] < lower) j1++;
while (j2 <= hi && arr[j2] - arr[i] <= upper) j2++;
count += j2 - j1;
}
long[] tmp = new long[hi - lo + 1];
int p = lo, q = mid + 1, idx = 0;
while (p <= mid && q <= hi) {
if (arr[p] <= arr[q]) tmp[idx++] = arr[p++];
else tmp[idx++] = arr[q++];
}
while (p <= mid) tmp[idx++] = arr[p++];
while (q <= hi) tmp[idx++] = arr[q++];
System.arraycopy(tmp, 0, arr, lo, idx);
return count;
}
}
import sys
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
n = int(input_data[idx]); idx += 1
prefix = [0] * (n + 1)
for i in range(1, n + 1):
prefix[i] = prefix[i - 1] + int(input_data[idx]); idx += 1
lower = int(input_data[idx]); idx += 1
upper = int(input_data[idx]); idx += 1
# 迭代归并排序(自底向上),避免递归栈溢出
ans = 0
size = 1
while size <= n:
lo = 0
while lo <= n:
mid = min(lo + size - 1, n)
hi = min(lo + 2 * size - 1, n)
if mid >= hi:
lo += 2 * size
continue
# 统计跨越左右两半的满足条件的对
j1 = mid + 1
j2 = mid + 1
for i in range(lo, mid + 1):
while j1 <= hi and prefix[j1] - prefix[i] < lower:
j1 += 1
while j2 <= hi and prefix[j2] - prefix[i] <= upper:
j2 += 1
ans += j2 - j1
# 归并
tmp = []
p, q = lo, mid + 1
while p <= mid and q <= hi:
if prefix[p] <= prefix[q]:
tmp.append(prefix[p]); p += 1
else:
tmp.append(prefix[q]); q += 1
while p <= mid:
tmp.append(prefix[p]); p += 1
while q <= hi:
tmp.append(prefix[q]); q += 1
prefix[lo:lo + len(tmp)] = tmp
lo += 2 * size
size *= 2
print(ans)
main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
const n = parseInt(lines[0]);
const vals = lines[1].split(' ').map(Number);
const [lower, upper] = lines[2].split(' ').map(Number);
const prefix = new Array(n + 1);
prefix[0] = 0;
for (let i = 1; i <= n; i++) {
prefix[i] = prefix[i - 1] + vals[i - 1];
}
console.log(mergeCount(prefix, 0, n, lower, upper));
});
function mergeCount(arr, lo, hi, lower, upper) {
if (lo >= hi) return 0;
const mid = lo + ((hi - lo) >> 1);
let count = mergeCount(arr, lo, mid, lower, upper)
+ mergeCount(arr, mid + 1, hi, lower, upper);
let j1 = mid + 1, j2 = mid + 1;
for (let i = lo; i <= mid; i++) {
while (j1 <= hi && arr[j1] - arr[i] < lower) j1++;
while (j2 <= hi && arr[j2] - arr[i] <= upper) j2++;
count += j2 - j1;
}
const tmp = [];
let p = lo, q = mid + 1;
while (p <= mid && q <= hi) {
if (arr[p] <= arr[q]) tmp.push(arr[p++]);
else tmp.push(arr[q++]);
}
while (p <= mid) tmp.push(arr[p++]);
while (q <= hi) tmp.push(arr[q++]);
for (let i = 0; i < tmp.length; i++) arr[lo + i] = tmp[i];
return count;
}

京公网安备 11010502036488号