题目大意:
给定一个数组,从左往右对数组中每个数减1,如果当前数为0就不对其操作
对这个数组有多少种排列方式,使得一个数不会被连续减两次
分类讨论:
1:如果数组中最大的数远远大于其他数(最大数–次大数>=2),就没有任意一种排列方式满足题意
2:如果数组中的最大数>=2(最大数不止一个)那么无论怎么排列都是满足题意的,答案就是数组数量的阶乘
3:存在一个最大数,至少存在一个次大数使得最大数-次大数==1
此时判断有哪些非法排列情况:显然,当次大数全部位于最大数的左边时,该排列是非法的
不理解的可以用 1 2 2 2 3 1 来模拟一下
此时就可以枚举最大数左边的空间(最大数的位置),来计算所有次大数位于最大数左边的情况下排列的数量
设当前最大数左边的空间是i,一共有n个数,次大数有num2个
如果i<num2,continue
此时,先对最大数的左右两边进行阶乘,计算排列
(i)!*(n-i-1)!
显然,非法的排列情况不止这些
由于次大数的位置被固定在最大数左边,还要考虑小于次大数的数(普通数)的分配问题:这些普通数一些被分配在左边,一些在右边,被分配在左边的数量为i-num2,一共有n-1-num2个普通数,这就变成一个组合问题了,预处理逆元,快速求出组合数,所以完整的式子是:
(i)!*(n-i-1)!*C(n - 1 - num2,i - num2)
求组合数的时候傻乎乎地去copy了一份lucas来求组合数,TLE了,我是傻逼QWQ
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
const int N = 2e5 + 10;
int a[N];
int n;
vector<int>g;
int ini[N];
int fact[N], infact[N];
bool cmp(int a, int b) {
return a > b;
}
int qmi(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1)
res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
void init() {
int k = 2e5;
ini[1] = ini[0] = 1;
for (int i = 2; i <= k; i++) {
ini[i] = (ini[i - 1] * i) % mod;
}
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ ) {
fact[i] = fact[i - 1] * i % mod;
infact[i] = infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
}
int get(int a, int b, int p) {
//求组合数
return fact[a] * infact[b] % p * infact[a - b] % p;
}
void solve() {
cin >> n;
g.clear();
for (int i = 1; i <= n; i++) {
cin >> a[i];
g.push_back(a[i]);
}
sort(g.begin(), g.end(), cmp);
if (g[0] - g[1] >= 2) {
cout << "0" << endl;
return;
}
int num1 = 0;
int num2 = 0;
for (auto i : g) {
if (i == g[0])
num1++;
if (i == g[0] - 1)
num2++;
}
if (num1 >= 2) {
cout << ini[n] << endl;
return;
}
int res = ini[n];
int del = 0;
for (int i = 1; i <= n - 1; i++) {
if (i < num2)
continue;//
else {
del = (del + ((ini[i] * get(n - 1 - num2, i - num2, mod) % mod) * ini[n - i - 1] ) % mod) % mod;
}
}
res = ((res - del) + mod) % mod;
cout << res << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
int t;
cin >> t;
while (t--) {
solve();
}
}