看懂了!这是一个很有趣的题目。让我先理解一下问题:
问题理解
我们有 n 个点 x₁ < x₂ < … < xₙ,对于每一对 (i, j) 画一条线段 [xᵢ, xⱼ]。问对于给定的 k,有多少个整数坐标点恰好被 k 条线段覆盖。
关键观察:
- 每个线段 [xᵢ, xⱼ] 覆盖了从 xᵢ 到 xⱼ 的所有整数点
- 点的数量很大(x ≤ 10⁹),不能枚举所有点
- k 可能很大(≤ 10¹⁸)
关键洞察
对于任意整数坐标点 p,它被多少条线段覆盖?
- 如果 p 恰好等于某个 xᵢ,那么所有以 xᵢ 为端点的线段都覆盖它
- 如果 p 在两个给定点之间(xᵢ < p < xᵢ₊₁),那么所有左端点 ≤ xᵢ 且右端点 ≥ xᵢ₊₁ 的线段都覆盖它
覆盖次数公式
对于点 p:
-
如果 p = xᵢ(给定点):
- 包含 p 作为左端点的线段数:右侧有 (n-i-1) 个点
- 包含 p 作为右端点的线段数:左侧有 i 个点
- 总覆盖数 = i * (n-i-1) + (n-1)
(因为每个其他点都可以与 p 形成线段)
-
如果 xᵢ < p < xᵢ₊₁:
- 只有那些左端点 ≤ xᵢ 且右端点 ≥ xᵢ₊₁ 的线段覆盖它
- 这样的线段数 = (i+1) * (n-i-1)
(左侧有 i+1 个点(包括 xᵢ),右侧有 n-i-1 个点(包括 xᵢ₊₁))
算法步骤
-
对于每个区间 [xᵢ, xᵢ₊₁]:
- 区间内整数点个数 = xᵢ₊₁ - xᵢ - 1
- 每个点都被 (i+1) * (n-i-1) 条线段覆盖
-
对于每个给定点 xᵢ:
- 被 i*(n-i-1) + (n-1) 条线段覆盖
- 每个这样的点只出现一次
-
用 map 统计每个覆盖次数出现的点数
-
对每个查询 k,直接输出 map[k]
C++ 实现
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
using ll = long long;
void solve() {
int n, q;
cin >> n >> q;
vector<ll> x(n);
for (int i = 0; i < n; i++) {
cin >> x[i];
}
map<ll, ll> cnt; // 覆盖次数 -> 点的个数
// 处理区间内的点
for (int i = 0; i < n - 1; i++) {
ll leftCount = i + 1; // 左侧点的个数(包括 x[i])
ll rightCount = n - i - 1; // 右侧点的个数(包括 x[i+1])
ll coverCount = leftCount * rightCount; // 每个区间内点的覆盖次数
// 区间内的整数点个数
ll pointsInInterval = x[i + 1] - x[i] - 1;
if (pointsInInterval > 0) {
cnt[coverCount] += pointsInInterval;
}
}
// 处理给定点
for (int i = 0; i < n; i++) {
ll leftCount = i; // 左侧点的个数
ll rightCount = n - i - 1; // 右侧点的个数
ll coverCount = leftCount * rightCount + (n - 1); // 每个给定点的覆盖次数
cnt[coverCount]++;
}
// 处理查询
for (int i = 0; i < q; i++) {
ll k;
cin >> k;
cout << cnt[k] << " ";
}
cout << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
验证示例
示例1
n=2, x=[101,200]
区间 [101,200]:pointsInInterval=200-101-1=98,coverCount=1*1=1 → 98个点被1条线段覆盖
点101:leftCount=0,rightCount=1,coverCount=0*1+(2-1)=1 → 1个点被1条线段覆盖
点200:leftCount=1,rightCount=0,coverCount=1*0+(2-1)=1 → 1个点被1条线段覆盖
总共:100个点被1条线段覆盖,0个点被2条线段覆盖
示例2
n=6, x=[1,2,3,5,6,7]
区间 [1,2]:points=0
区间 [2,3]:points=0
区间 [3,5]:points=5-3-1=1,coverCount=3*3=9 → 1个点(4)被9条线段覆盖
区间 [5,6]:points=0
区间 [6,7]:points=0
点1:coverCount=0*5+(6-1)=5 → 1个点被5条覆盖
点2:coverCount=1*4+5=9 → 1个点被9条覆盖
点3:coverCount=2*3+5=11 → 1个点被11条覆盖
点5:coverCount=3*2+5=11 → 1个点被11条覆盖
点6:coverCount=4*1+5=9 → 1个点被9条覆盖
点7:coverCount=5*0+5=5 → 1个点被5条覆盖
复杂度分析
- 时间复杂度:O(n log n + q log n),其中 log 来自 map 操作
- 空间复杂度:O(n)
对于 n ≤ 10⁵,这个算法足够快。

2367

被折叠的 条评论
为什么被折叠?



