小红的排列生成

[题目链接](https://www.nowcoder.com/practice/94c209756a8a4d3085ff64d62a4bb6fb)

思路

给定数组 ,每次操作可以选一个元素加 1(可操作任意次)。问最终能形成多少种不同的 的排列。

关键观察

一个排列 能被生成,当且仅当对每个位置 ,都有 (因为只能加不能减)。

所以问题转化为:有多少个 的排列 ,满足 对所有 成立?

贪心计数

从大到小排序(排序不影响答案,因为排列的分配可以任意对应)。设排序后为

按顺序为 分配排列值。对于 ,可选的值是 中尚未被使用的值:

  • 对应的候选集大小为
  • 前面已经分配了 个值,且这些值全部 ,所以恰好从候选集中占用了 个。
  • 因此第 步的可选数量为

由乘法原理,答案为:

$$

如果某一步可选数量 ,则答案为

样例验证

,排序后

答案 ,与样例一致。

复杂度

  • 时间复杂度:,排序的开销。
  • 空间复杂度:

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    const long long MOD = 1e9 + 7;
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; i++) cin >> a[i];
    sort(a.begin(), a.end(), greater<int>());
    long long ans = 1;
    for(int i = 0; i < n; i++){
        long long choices = (long long)(n - a[i] + 1) - i;
        if(choices <= 0){
            ans = 0;
            break;
        }
        ans = ans % MOD * (choices % MOD) % MOD;
    }
    cout << ans << endl;
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        final long MOD = 1000000007L;
        int n = sc.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) a[i] = sc.nextInt();
        Arrays.sort(a);
        for (int i = 0, j = n - 1; i < j; i++, j--) {
            int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
        }
        long ans = 1;
        for (int i = 0; i < n; i++) {
            long choices = (long)(n - a[i] + 1) - i;
            if (choices <= 0) {
                ans = 0;
                break;
            }
            ans = ans % MOD * (choices % MOD) % MOD;
        }
        System.out.println(ans);
    }
}