C Optimal Strategy 详解 【组合数学】【2021-ACM-ICPC-济南站】

刚打完的icpc济南站的C题 Optimal Strategy 比赛时没A出来,思路在比赛时是想对了.但还在想是个dp题,推式子题还是模拟题哈哈.好菜啊.
一般这种大数取模的就得推式子,找规律啥的.今天补了c题,题面如下
在这里插入图片描述

(一)题目大意
输入n,再输入n个数.E和M轮流取一个数使各自取到的数字总和最大,E和M都足够聪明.问取数的过程有几种.

(二)思路分析
要让和最大,自然想到每个数都不能太小.若直接贪心每次双方都取最大第一个样例都解释不通.
再模拟样例二发现暗示比较明显,每个数都出现偶数次,样例三便是奇偶出现次数都有.
开一个桶数组c存每个数字的出现次数,则第 i 个数出现 c[i] 次
对于出现奇数次的数,先手取走最大的一个数,后手取走次大的出现的奇数次的数(若上一步先手将最大数取完则后手取走当前最大的出现奇数次的数) 进行完上述操作则提取出各个不相同的数,而这些数对于最后结果无影响.剩下的出现奇数次的数转为出现偶数次
对于出现偶数次的数,双方轮流从大到小拿,一方拿走一个另一方会立刻拿走相同的数.取当前数时有c[i]/2种方案.
从小到大考察每一个数的出现次数,当前数的可选方案数为c[i]/2.比当前数小的数的出现次数和sum可直接作为总和选取.
大数一定可在小数之前选取,故每个数的方案为C(c[i]/2+sum,c[i]/2).
每个数内部排列方案数为 c[i]!
最终结果如下
在这里插入图片描述
本题相比推理,更侧重过程的理解和模拟,结合实际去考虑

(三)AC代码
求阶乘可参见下面f[]数组的方法,我直接写的jiecheng函数超时了
处理组合数时如下直接求

**#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <string>
#include <cstring>

using namespace std;
typedef long long ll;
const int N = 1e6+10;
const int mod = 998244353;

ll c[N],a[N];
int maxx;
ll sum = 0;
ll f[N], unf[N];

//ll jiecheng(ll a){
//	ll ans = 1;
//	for(int i=1;i<=a;i++){
//		ans = ans * i % mod; //别忘取模%mod,否则 
//	}
//	return ans;
//}

ll zuhe(ll a,ll b){
    return (ll)f[a] * unf[b] % mod * unf[a - b] % mod;
}

int main(){
	//下面为求阶乘预处理的方法,速度快,否则jiecheng函数超时 
	unf[0] = unf[1] = f[0] = 1;
    for (int i = 1; i < N; i ++) f[i] = (ll) f[i - 1] * i % mod;
    for (int i = 2; i < N; i ++) unf[i] = (ll)(mod - mod / i) * unf[mod % i] % mod;
    for (int i = 1; i < N; i ++) unf[i] = (ll) unf[i - 1] * unf[i] % mod;
	ll res = 1;
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		int x;
		cin>>x;
		c[x]++;
		if(maxx < x){ //输入最大数 
			maxx = x;
		}
	//	cout<<maxx<<endl;
	}
	
//	cout<<endl;

//	for(int i=1;i<=maxx;i++){
//		cout<<i<<"  "<<c[i]<<" ***  ";
//	}
//	cout<<endl;
	
	ll shang,xia;
	for(int i=1;i<=maxx;i++){
		if( c[i] != 0){
//			cout<<c[i]<<endl;
			shang = floor(c[i]/2);
			xia = shang + sum;
//			cout<<shang<<endl<<xia<<endl;
//			cout<<jiecheng(c[i])<<endl;
			
//			cout<<zuhe(xia,shang)<<endl;
//			cout<<endl;
			res = res * f[c[i]]%mod * zuhe(xia,shang)%mod;
			sum += c[i];
		}
	}
	
//	cout<<endl;
	cout<<res<<endl;

	return 0;
}

/*
3
1 2 2

6
1 3 2 2 3 1

12 
1 1 4 5 1 4 1 9 1 9 8 10
*/
**

在这里插入图片描述

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值