魔法排列

题目

题目连接:魔法排列

众所周知,集合 {1 2 3 … N} 有 N! 种不同的排列,假设第 i 个排列为 Pi 且 P[i][j] 是该排列的第 j 个数。

将 N 个点放置在 x 轴上,第 i 个点的坐标为 xi 且所有点的坐标两两不同。

对于每个排列(以 Pi 为例),可以将其视为对上述 N 个点的一种遍历顺序,即从第 P[i][1] 个点出发,沿直线距离到达第 P[i][2] 个点,再沿直线距离到达第 P[i][3] 个点,以此类推,最后到达第 P[i][N] 个点,将该路线的总长度定义为 L(Pi),那么所有 N! 种路线的总长度之和是多少,即 L(P1) + L(P2) + L(P3) + ... + L(PN!) 的结果是多少?

输入描述

第一行包含一个整数 N,1 ≤ N ≤ 10^5。
第二行包含 N 个空格隔开的整数 x1 到 xN,0 ≤ x1 < x2 < x3 < ... < xN ≤ 10^9。

输出描述

输出 L(P1) + L(P2) + L(P3) + ... + L(PN!) 对 10^9 + 7 取模后的结果。

样例输入

3
0 1 3

样例输出

24

说明

P1={1 2 3},P2={1 3 2},P3={2 1 3},P4={2 3 1},P5={3 1 2},P6={3 2 1};
L(P1)=3,L(P2)=5,L(P3)=4,L(P4)=5,L(P5)=4,L(P6)=3。

AC 代码

import java.util.Scanner;

/**
 * 思路:这道题的主要思路是排列组合
 *
 *      例如有4个数[1, 2, 3, 4],那么所有的排列组合为:
 *      1 2 3 4 --- 1 2 4 3 --- 1 3 2 4 --- 1 3 4 2 --- 1 4 2 3 --- 1 4 3 2
 *      2 1 3 4 --- 2 1 4 3 --- 2 3 1 4 --- 2 3 4 1 --- 2 4 1 3 --- 2 4 3 1
 *      3 1 2 4 --- 3 1 4 2 --- 3 2 1 4 --- 3 2 4 1 --- 3 4 1 2 --- 3 4 2 1
 *      4 1 2 3 --- 4 1 3 2 --- 4 2 1 3 --- 4 2 3 1 --- 4 3 1 2 --- 4 3 2 1
 *
 *      可以发现,任意连个数相邻的情况都为 6,例如有 6 个 1 2
 *      这个 6 是怎么来的呢?可以把 1 2 捆绑在一起,那么就变成了如何将 1 2 3 4 放在三个坑中
 *      得到 A(3, 3) = 6 种
 *
 *      其他的方式也一样,另外 1 2 和 2 1 是两种不同的组合,在计算时只需要乘以 2 即可
 */
public class Test {

    public static long mod = 1000000007;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int[] x = new int[N];
        for (int i = 0; i < N; i++) {
            x[i] = scanner.nextInt();
        }
        int n = factorial(x.length - 1);

        /**
         * 时间复杂度 O(N)
         *
         * 思想:
         *      例如 1 2 3 4 要计算 len(1, 4) + len(2, 4) + len(3, 4)
         *      = (4 - 1) + (4 - 2) + (4 - 3)
         *      = 4 * 3 - (1 + 2 + 3)
         */
        long sum = 0, tem = 0, a;
        for (int i = 1; i < x.length; i++) {
            tem = (tem + x[i - 1]) % mod;
            // 这里不能直接写成 a = x[i] * i;
            // 因为 a 的类型为 long,而 x[i] 和 i 都是 int 类型
            // 两个 int 类型的数相乘就有导致溢出
            a = x[i] * (long)i;
            sum = (sum + a - tem) % mod;
        }
        sum = (sum << 1) % mod;
        sum = (sum * n) % mod;
        System.out.println(sum);
    }

    // 计算 N!
    public static int factorial (int n) {
        long result = 1;
        for (int i = 1; i <= n; i++) {
            /**
             * 这里可以这样计算的原因是
             *      (a * b) % c = ((a % c) * (b % c)) % c
             */
            result = (result * i) % mod;
        }
        return (int) result;
    }
}
posted @ 2019-08-26 09:06  xwxw  阅读(270)  评论(0编辑  收藏  举报