小U的走排列问题 | 豆包MarsCode AI刷题

36 阅读2分钟

问题描述

数轴上有 nn 个点 aia_i,小U初始在原点。她希望按照一定的顺序访问这些点。小U想知道在所有不同的访问顺序中,走过的路径的总和是多少。每种顺序对应的路径长度等于她从原点出发依次访问这些点所走的距离之和。

例如,有三个点 [1,3,5][1,3,5],按照顺序 a1,a2,a3a1,a2,a3 访问,走过的路径为:10+31+53=510+31+53=5∣1−0∣+∣3−1∣+∣5−3∣=5∣1−0∣+∣3−1∣+∣5−3∣=5

按照顺序 a1,a3,a2a1,a3,a2 访问,走过的路径为:10+51+35=710+51+35=7∣1−0∣+∣5−1∣+∣3−5∣=7∣1−0∣+∣5−1∣+∣3−5∣=7

所有的访问顺序有 n!n! 种。

你需要计算所有不同的访问顺序中走过的路径总和。答案对 109+710^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

问题分析

暴力枚举 O(n!)O(n!)

正如题目中所说,所有的访问顺序一共有 n!n! 种,我们可以通过经典算法dfs枚举所有可能存在的访问顺序,然后模拟求取每一个排列的路径长度,相加即为答案。

这样的方法虽然简单,但在n稍大一点的情况下就容易出现超时,我们可以选择一些其他方式计算。

组合数学+前缀和 O(nlogn)O(nlogn)

使用组合数学的方式优化计算过程,我们可以枚举任意两点之间的边,我们发现在存在的所有排列中,一共有(n1)!(n-1)!个排列是包含这条边的,这样我们就略去了重复计算某一条边的值,转而直接计算这条边出现的次数,快速求取答案。

这个做法的时间复杂度O(n2)O(n^2)的,也足以通过该题目,但仍然不够极致。

我们发现,对于每一条边,都有(n1)!(n-1)!个排列是包含这条边的。那我们直接计算所有点与其他点的距离的总和,我们就可以直接计算每一个点对答案带来的贡献。

这里采用排序+前缀和的方式,计算每个点和所有坐标小于它的点的距离之和,即可获得答案。

参考代码

typedef long long ll;
const int mod = 1e9+7;
int solution(int n, vector<int> a) {
    sort(a.begin(),a.end());
    ll len = 0, ans = 0, t = 1;
    for(int i=1;i<n;i++) t = t * i % mod;
    ans += a[0] * t % mod;
    for(int i=1;i<n;i++) {
        len += (a[i] - a[i-1]) * i % mod;
        len %= mod;
        ans = (ans + len * t * 2 + a[i] * t) % mod;
    }
    return ans % mod;
}