问题描述
数轴上有 nn 个点 aiai,小U初始在原点。她希望按照一定的顺序访问这些点。小U想知道在所有不同的访问顺序中,走过的路径的总和是多少。每种顺序对应的路径长度等于她从原点出发依次访问这些点所走的距离之和。
例如,有三个点 [1,3,5][1,3,5],按照顺序 a1,a2,a3a1,a2,a3 访问,走过的路径为:∣1−0∣+∣3−1∣+∣5−3∣=5∣1−0∣+∣3−1∣+∣5−3∣=5。
按照顺序 a1,a3,a2a1,a3,a2 访问,走过的路径为:∣1−0∣+∣5−1∣+∣3−5∣=7∣1−0∣+∣5−1∣+∣3−5∣=7。
所有的访问顺序有 n!n! 种。
你需要计算所有不同的访问顺序中走过的路径总和。答案对 10^9+7 取模。
测试样例
样例1:
输入:
n = 3,a = [1, 3, 5]
输出:50
样例2:
输入:
n = 4,a = [1, 2, 4, 7]
输出:324
样例3:
输入:
n = 2,a = [2, 6]
输出:16
Python解题
思路说明
题目要求计算所有不同的访问顺序中走过的路径总和。每种访问顺序对应的路径长度等于从原点出发依次访问这些点所走的距离之和。由于访问顺序有 n! 种,直接枚举所有排列并计算路径长度是不可行的。因此,我们需要通过数学推导来简化问题。
首先,考虑每个点在所有排列中出现的次数。对于任意一个点 ai,它在所有排列中出现的次数是 n! 次。接下来,考虑每两个点之间的距离在所有排列中出现的次数。对于任意两个点 ai 和 aj,它们之间的距离 ∣ai−aj∣ 在所有排列中出现的次数是 2×(n−1)! 次(因为每两个点之间的距离在排列中会出现两次)。
通过这些推导,我们可以将问题转化为计算每个点在所有排列中的贡献以及每两个点之间距离在所有排列中的贡献,最终将这些贡献相加即可得到答案。
解题过程
- 计算阶乘:首先计算 n! 并存储在
fac
数组中,用于后续计算每种排列的贡献。 - 计算每个点的贡献:对于每个点 ai,它在所有排列中出现的次数是 n! 次,因此它的贡献是 ai×(n−1)!。将这些贡献累加到
ans
中。 - 计算每两个点之间距离的贡献:对于每两个点 ai 和 aj,它们之间的距离 ∣ai−aj∣ 在所有排列中出现的次数是 2×(n−1)! 次。将这些贡献累加到
ans
中。 - 取模运算:由于结果可能非常大,每次计算后都对 10^9+7 取模。
复杂度分析
- 时间复杂度:代码中包含两层循环,外层循环遍历 n 个点,内层循环遍历 n−i−1 个点,因此总的时间复杂度为 O(n^2)。
- 空间复杂度:代码中使用了一个长度为 n+1 的数组
fac
来存储阶乘,因此空间复杂度为 O(n)。
知识点扩展
- 排列组合:题目涉及到排列组合的知识,特别是如何计算每个元素在所有排列中的出现次数。
- 数学推导:通过数学推导,我们可以将问题转化为计算每个点在所有排列中的贡献以及每两个点之间距离在所有排列中的贡献。
- 取模运算:由于结果可能非常大,需要对 10^9+7 取模,这是处理大数问题时常用的技巧。
代码实现
import math
mod = 10**9 + 7
def solution(n, a):
ans = 0
fac = [1] * (n+1)
for i in range(1, n+1):
fac[i] = fac[i-1] * i % mod
for i in range(n):
ans = (ans + a[i] * fac[n-1] % mod) % mod
for j in range(i+1, n):
temp = abs(a[i] - a[j])
ans = (ans + 2 * fac[n-1] * temp % mod) % mod
return ans
if __name__ == '__main__':
print(solution(3, [1, 3, 5]) == 50)
print(solution(4, [1, 2, 4, 7]) == 324)
print(solution(2, [2, 6]) == 16)