【2021 ICPC Asia Jinan 区域赛】 C Optimal Strategy推公式-组合数-逆元快速幂
题目链接 题目详情 (pintia.cn)
题目
题意
有n个物品在他们面前,编号从1自n.两人轮流移走物品。在移动中,玩家选择未被拿走的物品并将其拿走。当所有物品被拿走时,游戏就结束了。任何一个玩家的目标是最大化他们拿走的物品的价值之和。
二人都足够聪明,有多少可能的游戏过程?结果取模998244353.
如果存在一些整数相等,但是下标不一样,算为两种方式。
题解
此题可以抽象为有序序列,第一个位置第一次被拿走,第二个位置第二次被拿走,后者亦然,求有多少种符合条件的序列
1. 最大值若为单数个,第一个人一定拿走,对于种数并没有贡献
2. 最大值若为双数个,第一个人拿走一个,后面人一定也要拿走一个
3. 每次拿完,都可以抽象为前两种。
以下为用高中排列组合思考问题:
AC代码
组合数用逆元求,如果按正常阶乘算,若 被除数取模过,那么结果也就不一样了
// #include#include #include #include #include <string> #include #include using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N = 1e6+10; const LL mod = 998244353; int st[N]; LL fact[N], infact[N]; LL qmi(LL x, LL k)//快速幂 { LL res = 1; while(k) { if(k&1) res = (LL)res * x %mod; k >>= 1; x = (LL)x * x % mod; } return res; } void init(int n) { fact[0] = 1, infact[0] = 1; for(int i = 1; i <= n; i ++) { fact[i] = fact[i-1]*i%mod;//n的阶乘 infact[i] = infact[i-1]*qmi((LL)i, mod-2)%mod;//逆元 } return; } LL zuhe(int n, int m)//求组合数 { return fact[n] * infact[n-m] %mod* infact[m]%mod; } int main(){ vector a; int n; cin >> n; init(n); int idx = 0; for(int i = 0; i < N; i ++) st[i] = -1; for(int i = 0; i < n; i ++) { int x; cin >> x; if(st[x]>=0) a[st[x]].second ++;//记录每个数出现了几次 else { a.push_back({x, 1}); st[x] = idx ++; } } sort(a.begin(), a.end()); int sum = 0; LL res = 1; for(int i = a.end()-a.begin()-1; i >= 0; i --)sum+=a[i].second; //数学公式实现过程 for(int i = a.end()-a.begin()-1; i >= 0; i --) { sum -= a[i].second; int c = a[i].second; if(c >= 2) res = res * fact[c]%mod * zuhe((sum + c/2), c/2)%mod; } cout << res <<endl; return 0; }