Problem 1160
给定 $N$ 个区间 $[a_i, b_i]$,其中 $0 \le a_i \le b_i \le M$。
对于 $0$ 到 $2M$ 之间的每一个整数 $k$,计算有多少对索引 $(i, j)$ 满足:
数据范围:
条件 $a_i + a_j \le k \le b_i + b_j$ 实际上是在描述两个区间的和。
如果 $k$ 落在 和区间 内,则这对 $(i, j)$ 贡献 +1。
直接枚举所有可能的区间对 $(i, j)$:
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
int start = a[i] + a[j];
int end = b[i] + b[j];
// 在差分数组 [start, end] 区间 +1
}
}
复杂度分析:
$N$ 很大,但是 $M$ 很小!
$M \le 5000$
// 统计频率
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
scanf("%d", &b[i]);
cnta[a[i]]++; // 统计起始点出现次数
cntb[b[i]]++; // 统计结束点出现次数
}
// 核心:O(M^2) 枚举所有可能的和
for (int i = 0; i <= 2*m; i++) // i 代表两数之和
for (int j = 0; j <= i; j++) { // j 代表第一个数
// j 和 i-j 是一对数。cnta[j]*cnta[i-j] 是这对数组合出现的次数
// 如果 j 或 i-j 超过 M,数组值为0,不影响结果
// 在和为 i 的位置开始,区间数增加
diff[i] += cnta[j] * cnta[i-j];
// 在和为 i 的位置结束(的下一位),区间数减少
// 注意:这里处理的是 End Sum
diff[i+1] -= cntb[j] * cntb[i-j];
}
// 前缀和还原答案
ll ans = 0;
for (int i = 0; i <= 2*m; i++) {
ans += diff[i];
printf("%lld\n", ans);
}
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
// 数据规模定义
int n, m;
int a[200005], b[200005];
ll cnta[10005], cntb[10005]; // 频率数组,大小 > 2*M
ll diff[10005]; // 差分数组
int main() {
// 1. 输入与频率统计
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
scanf("%d", &b[i]);
cnta[a[i]]++;
cntb[b[i]]++;
}
// 2. 计算卷积与构建差分数组
// 枚举所有可能的和 i (从 0 到 2M)
for (int i = 0; i <= 2*m; i++) {
for (int j = 0; j <= i; j++) {
// 边界检查由数组大小和数据特性自然保证(越界值为0)
// 但严谨来说,只有 j<=M 且 i-j<=M 时才有意义
if (j <= m && (i-j) <= m) {
// Start Sum 贡献 +
diff[i] += cnta[j] * cnta[i-j];
// End Sum 贡献 - (在 i+1 处)
diff[i+1] -= cntb[j] * cntb[i-j];
}
}
}
// 3. 前缀和计算最终答案并输出
ll ans = 0;
for (int i = 0; i <= 2*m; i++) {
ans += diff[i];
printf("%lld\n", ans);
}
return 0;
}
这道题是经典的"值域小,数据量大"的类型。