C. Jury Meeting(1569C)(组合数学)(正向思维)
题目来源:C. Jury Meeting
题意:
有n个人,每个人的序号为1 ~ n,第 i 个人有 a[i] 个问题,每次一个人只能问一个问题
将这 n 个人排序,寻找有多少种序列使得不断从左到右提问题,不会出现同一个人连续问两次问题
思路:
首先通过样例我们不难发现,当最大的两个数之差小于等于1时答案不为0
- 差大于 1 时,答案为 0
- 差等于 0 时,答案为 n!
- 差等于 1 时,记最大的两个数为 x 和 y,其中 x > y
- 这时至少要有一个 y 排在 x 的后边才有解(参考样例 1 2),剩下的随便排列即可
- 那么我们先统计出 y 的个数 num,这 num 个 y 随机排列,之后将 x 插空,由于至少有一个 y 在 x 后,所以此时相当于有 num 个空, 所以 ans * num! * num
- 插入 x 后,有 num + 2 个空,之后每插入一个数,空隙就多一个,还剩余 n - num - 1 个数,那么也就是说答案再乘上 (num + 2) * (num + 3) * … * (num + 2 + n - num - 1) 即可
AC代码
#include <bits/stdc++.h>
#define endl "\n"
#define rep(i, m, n) for (int i = (m); i <= (n); ++i)
#define rrep(i, m, n) for (int i = (m); i >= (n); --i)
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 998244353;
int n, x[N];
ll f[N];//统计阶乘
void solve() {
scanf("%d", &n);
rep(i, 1, n) scanf("%d", &x[i]);
sort(x + 1, x + 1 + n, greater<int>());//大 -> 小
if (x[1] - x[2] > 1) { // 比较最大的两个数
puts("0"); return;
}
ll ans = 1;
if (x[1] == x[2]) ans = f[n]; //n!
else {
int num = 1, vis = 3;//num:第二大的数的个数
//vis:统计之后插入n - num - 1个数时,空隙的个数
rep(i, 3, n) //统计个数
if (x[i] == x[2]) num++, vis++;
else break;
ans = f[num] * num % mod;
while (n - num - 1) {
ans = ans * vis % mod;
n--; vis++;//插入一个数,空隙多一
}
}
printf("%lld\n", ans);
}
int main() {
f[0] = 1;
rep(i, 1, n) f[i] = f[i - 1] * i % mod;
int t; cin >> t;
while (t--) solve();
return 0;
}