小U走排列问题

问题描述

数轴上有 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)! 次(因为每两个点之间的距离在排列中会出现两次)。

        通过这些推导,我们可以将问题转化为计算每个点在所有排列中的贡献以及每两个点之间距离在所有排列中的贡献,最终将这些贡献相加即可得到答案。

解题过程

  1. 计算阶乘:首先计算 n! 并存储在 fac 数组中,用于后续计算每种排列的贡献。
  2. 计算每个点的贡献:对于每个点 ai​,它在所有排列中出现的次数是 n! 次,因此它的贡献是 ai​×(n−1)!。将这些贡献累加到 ans 中。
  3. 计算每两个点之间距离的贡献:对于每两个点 ai​ 和 aj​,它们之间的距离 ∣ai​−aj​∣ 在所有排列中出现的次数是 2×(n−1)! 次。将这些贡献累加到 ans 中。
  4. 取模运算:由于结果可能非常大,每次计算后都对 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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值