本文最后更新于 9 个月前,文中所描述的信息可能已发生改变。
E. Power of Points 解题思路
核心问题分析与数学转化
本题要求对于给定的点集 ,对集合中每一个 ,计算总功率和 。其中 定义为与点 相交的线段 的数量。
1. 功率和与线段长度的关系
一个关键的观察是,一个线段 (不妨设 ) 覆盖的整数点数量(即对总功率和 的贡献)恰好是其长度 。
线段 的长度为:
因此,总功率和 实际上等于所有 条线段长度之和:
总和可以简化为:
我们的核心任务,便转化成了对每一个 ,高效计算 。
2. 离散化与结果缓存
由于输入点 的坐标范围高达 ,但点的数量 较小(),我们采用以下策略来优化计算:
- 数据预处理:对原始输入进行排序,以便进行差分计算。
- 结果缓存:使用
std::map<long long, long long> data_map存储每个不重复的 坐标及其对应的总功率和,以应对原始输入中存在的重复点。
核心优化:差分递推计算 之和
为了实现 的时间复杂度(主要耗费在排序上),我们使用差分(Differential)思想。
设 。我们对排序后的去重点 和 进行分析,差值 。通过已知的 来快速计算相邻点 。
递推公式的建立
当 从 变化到 时,每个 对总和的贡献变化量 为:
对于 (左侧):
(贡献增加 )
对于 (右侧):
(贡献减少 )
递推关系:
设 为 的点数, 为 的点数。总和变化量 为:
3. 计算第一个点 的初始值
对于排序后的第一个点 ,所有 ,因此 。
初始总功率和 为:
CPP 代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
void solve() {
// N: 数据点的数量
long long data_number;
// current_sum: 用于保存 Σ|s - x_i| 的当前值 (距离和)
long long current_sum = 0;
cin >> data_number;
// data: 存储原始输入坐标,用于排序和递推
vector<long long> data(data_number);
// ans_order: 存储原始输入顺序,用于最后按原顺序输出
vector<long long> ans_order(data_number);
// result_map: 存储去重后的 s 坐标及其对应的总功率和(结果)
map<long long, long long> result_map;
// 1. 读取数据并计算所有 x_i 的总和,同时保存原始顺序
for (long long i = 0; i < data_number; i++) {
cin >> data[i];
ans_order[i] = data[i]; // 保存原始顺序
current_sum += data[i]; // 累加所有 x_i (Σx_i)
}
// 2. 排序,为差分递推做准备
sort(data.begin(), data.end());
// 3. 计算第一个点 data[0] 的初始 Σ|s - x_i|
// current_sum = (Σx_i) - n * data[0]
current_sum = current_sum - data[0] * data_number;
// S_0 (总功率和) = Σ|s_0 - x_i| + n
long long s0_total_power = data_number + current_sum;
// 将第一个点 data[0] 的结果存入 Map
result_map[data[0]] = s0_total_power;
// 4. 差分递推计算后续点 S_i 的结果
for (long long i = 0; i < data_number - 1; i++) {
if (data[i + 1] != data[i]) {
// 变化量 Δ = data[i+1] - data[i]
long long delta = data[i + 1] - data[i];
// L: 左侧点数,即 x_j <= data[i] 的数量 (包含 data[0] 到 data[i])
long long L = i + 1;
// R: 右侧点数,即 x_j > data[i+1] 的数量
// R = 总点数 - (i + 2)
long long R = data_number - (i + 2);
// 递推核心: S_next - S_curr = L * Δ - R * Δ
long long diff = (L * delta) - (R * delta);
// 更新 current_sum (Σ|s - x_i|)
current_sum += diff;
// 更新 S(s) 的总功率和 (Σ|s - x_i| + n)
result_map[data[i + 1]] = current_sum + data_number;
}
}
// 5. 按原始输入顺序输出结果
for (long long i = 0; i < data_number; i++) {
cout << result_map[ans_order[i]] << ' ';
}
cout << '\n';
}
int main() {
// 优化 I/O 速度
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
long long test;
cin >> test;
while (test--) {
solve();
}
}