[CF-Edu113]C. Jury Meeting
题目
\(n\)个人,第\(i\)个人有\(a_i\)句话要说,你可以安排一个发言顺序(是一个\(1\sim n\)的排列),如果有人有话说,就进行发言,轮到第\(i\)个人时,若TA有话说,就说一句,不然就跳过.一个发言顺序良好,当且仅当任何一个人不会连续发言,问良好的发言顺序的数量,取模.
没想到in a row还可以翻译成连续
思路
题目说的这么绕,简化一下吧.
设\(a\)数组的最大值出现了\(num1\),次大值出现了\(num2\)次,若\(num1 > 1\),不管怎么排列都是合法的,方案数就是\(n!\).否则,又有两种情况:①最大值减次大值大于1,不管怎么都是不合法的,输出0;②最大值恰好比次大值大1,这也是需要我们详细讨论的.
我们单独把\(a\)的最大值和次大值单独拿出来分析:
一个顺序要是合法,最大值就不能放在最后面(注意这里"最后"的含义,已经忽略了最大值次大值以外的东西),显然,不合法的排列方案数就是\(num2!\)(最大值要放在最后面).
最大值次大值以外的,是可以随便排的,方案数是\((n-num1-num2)!\).
然后把两个排列合并起来,使原来在同一个排列的两个元素相对顺序不改变(我语文不好),的方案数就是\(C_n^{num2+num1}\)(具体可以看这个:[ABC217]F - Make Pair).
综上,这个情况的答案就是\(num2!\cdot (n-num1-num2)! \cdot \frac{n!}{(num2+num1)!\ (n-num2-num1)!}=num2!\cdot \frac{n!}{(num2+num1)!}\)
代码
#include <iostream>
#include <cstdio>
using namespace std;
template <class T>
T read() {
T re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' ,c = getchar();
return negt ? -re : re;
}
typedef long long LL;
const LL mod = 998244353;
const int N = 200010;
int n;
int a[N];
LL fac[N] , inv[N];
LL Pow(LL a , int p) {
LL res = 1;
while(p) {
if(p & 1)
res = res * a % mod;
a = a * a % mod;
p >>= 1;
}
return res;
}
int main() {
fac[0] = inv[0] = 1;
for(int i = 1 ; i < N ; i++)
fac[i] = fac[i - 1] * i % mod;
for(int i = 1 ; i < N ; i++)
inv[i] = Pow(fac[i] , mod - 2);
int T = read<int>();
while(T--) {
n = read<int>();
for(int i = 1 ; i <= n ; i++)
a[i] = read<int>();
int maxa = 0;
for(int i = 1 ; i <= n ; i++)
if(maxa < a[i])
maxa = a[i];
int num1 = 0 , num2 = 0;
for(int i = 1 ; i <= n ; i++)
if(a[i] == maxa)
++num1;
else if(a[i] == maxa - 1)
++num2;
if(num1 > 1) {
printf("%d\n" , fac[n]);
} else {
printf("%d\n" ,(
fac[n] -
fac[num2]
* fac[n - num2 - 1] % mod
* fac[n] % mod
* inv[num2 + 1] % mod
* inv[n - num2 - 1] % mod
+ mod) % mod
);
}
}
return 0;
}