#include "iostream"
#include "cstring"
#include "algorithm"
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int mod = 998244353;
int c[N]; //求某个数有几个
ll f[N], inv[N]; //f求到哪个数的时候有几种组合 inv求 /f[i]对mod取模的值
ll fact[N];
ll fast_pow(int x, int n){ //快速幂
ll t = 1;
while(n){
if(n & 1) t = t * x % mod;
x = 1ll*x * x % mod;
n >>= 1;
}
return t;
}
void init(){ //预处理阶乘和阶乘的逆元
fact[0] = 1;
for(int i = 1; i <= N; i++){
fact[i] = fact[i - 1] * i % mod;
inv[i] = fast_pow(fact[i], mod - 2);
}
}
inline ll C(int n, int m){ //组合数
if(m == 0 || n == m || n == 0) return 1ll;
return 1ll* fact[n] * inv[m] % mod * inv[n - m] % mod;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
memset(c, 0, sizeof(c));
init();
int n, x, ann;
cin>>n;
int ma = -1, mi = 1e6 + 10;
for(int i = 0; i < n; i++){
cin>>x;
if(x > ma) ma = x;
if(x < mi) mi = x;
c[x] ++;
}
ll cnt = 0;
f[mi] = fact[c[mi]];
cnt = c[mi];
for(int i = mi + 1; i <= ma ; i++){
f[i] = f[i - 1] * C(cnt + c[i]/ 2, c[i] / 2)% mod * fact[c[i]]%mod;
cnt += c[i];
}
cout<<f[ma]<<endl;
return 0;
}
这道题目就是一个贪心+动态规划的题目,和队友做了好久没做出来,后来看的大佬的题解,就是如果当前价值最大的是奇数的话,那就一定先拿最大的,如果是偶数的话,可以随便拿,但是一旦你前面的人拿了最大的,你就一定要也跟着拿最大的,否则就会输,所以就是两个相同价值的一定要成对出现,这样的话,我们从最小的开始(因为最小的话,拿的顺序其实是个全排列,这个既然都是最小了就不在意谁先拿谁后拿,反正都要拿走), 然后在这个的基础上,每增加一个就添加进一个n对(可以有单)的排列组合,以此类推,直到排列完最后一种数字。
值得注意的是,在我求组合数的时候运用了一个费马小定理来求,结论就是:
a * a**(p - 2) % p = 1
a / b % p = a * b ** (p - 2) % p
费马小定理 具体的可以看下这个大佬的博客!
好了,今天做的这套2021年济南站的ICPC真的是感觉,就是考高数啊,队友一个数院的小姐姐直接把看家本领都使出来了,果然还得是数院大佬啊,今天又学习了一个新的知识点,虽然说浪费的时间有点多吧。