1 条题解
-
0
题解
绝对值在平方后不会影响结果,因此每对音叉产生的差异能量可以直接写成 .
算法 0:枚举数对
枚举所有 ,把 加入答案并及时取模.时间复杂度为 ,空间复杂度为 .
预期通过 subtask 1、2,预期得分为 分.
算法 1:按照数值统计
当 时,只有数值不同的数对产生贡献,答案等于零的数量乘以一的数量,再对 取模.预期通过 subtask 3,预期得分为 分.
当 时,可以统计每个值的出现次数 ,再枚举两个不同的值:
时间复杂度为 ,其中 ,空间复杂度为 .这也包含二值序列的情况.
预期通过 subtask 3、4,预期得分为 分.
算法 2:展开平方
先展开每个数对的贡献:
在所有无序数对中,每个 恰好出现 次,因此
另一方面,平方和满足
$$\left(\sum_{i=1}^{n}a_i\right)^2 =\sum_{i=1}^{n}a_i^2+2\sum_{i<j}a_ia_j.$$将两式合并,可以得到
$$\sum_{i<j}(a_i-a_j)^2 =n\sum_{i=1}^{n}a_i^2-\left(\sum_{i=1}^{n}a_i\right)^2.$$扫描序列时维护 和 ,最终计算 并将负余数调整到 即可.
不能先用整数保存未经取模的 和 :完整数据中 可以达到 , 已经远超 64 位整数范围.实现时应让每个读入值先对模数取模,并在每次加法后维护 的余数.此时单次乘法的两个因子都小于 ,使用 64 位有符号整数即可安全完成乘法.另外,C++ 中负数取模仍可能为负,因此最终答案小于 时要加一次模数.大量重复值不需要特殊处理,相等数对的贡献会在公式中自然抵消.
正确性证明
平方展开对每个数对都成立;汇总后,每个平方项和每个乘积项出现的次数均由上面两条等式精确统计.因此最终公式与题目要求的所有数对贡献之和完全相等,取模不会改变同余结果,算法正确.
时间复杂度为 ,空间复杂度为 .
预期通过所有 subtask,预期得分为 分.
参考代码
#include <cstdint> #include <iostream> constexpr std::int64_t MOD = 1000000007LL; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::int64_t n; std::cin >> n; std::int64_t sum = 0; std::int64_t square_sum = 0; for (std::int64_t i = 0; i < n; ++i) { std::int64_t x; std::cin >> x; x %= MOD; sum += x; if (sum >= MOD) sum -= MOD; square_sum = (square_sum + x * x) % MOD; } std::int64_t answer = ((n % MOD) * square_sum - sum * sum) % MOD; if (answer < 0) answer += MOD; std::cout << answer << '\n'; return 0; }
- 1
信息
- ID
- 258
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 3
- 标签
- 递交数
- 46
- 已通过
- 4
- 上传者