2021 icpc 济南站C Optimal Strategy

problem

一个长度为n的数组a,两个人轮流取,想让自己的拿的总和尽量大,都有最优策略,问有几种拿取的方案数。

solution

赛场上做出来了,现在会看又不会了。
先手和后手的最后拿的棋子是定的。
当最大的数出现了奇数次,先手必须要拿这个最大的,不然的话,本来是先手拿x+1个,后手拿x个,现在成了先手拿x个后手拿x+1个,亏大了。
当最大的数出现了偶数次,可以先不用拿,去拿别的,不管谁第一次拿了一个,后手肯定会可以跟着拿了这个最大的,所以说这个最大值是安全的,谁也不会吃亏。
不先拿大的拿小其实和直接两人每次都取大的是一样的结果。
比如3 3 2
A先拿2,B再拿3,A再拿3
虽然你先拿一个大的失去了先手顺序,但是你拿到了一个以后需要拿的,最大的是偶数,是不会少拿的,所以就是说和每次取大的一样。
1nCsum+t[i]2t[i]2

sum是1到i-1的ti的和,t[i]=1n[i==a[i]]

code

#include <bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;++i)
#define ll long long
using namespace std;
const int _=1e6+7;
const int mod=998244353;
int read() {
    int x=0,f=1;char s=getchar();
    for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    return x*f;
}
int n, ans, a[_], t[_];
int jc[_], inv_jc[_];
int ksm(int a,int b) {
    int ans=1;
    while(b) {
        if(b&1) ans=1ll*a*ans%mod;
        a=1ll*a*a%mod;
        b>>=1;
    } return ans;
}
int C(int n, int m) {
    if(m > n || n < 0 || m < 0) return 0;
    return 1ll * jc[n] * inv_jc[m] % mod * inv_jc[n - m] % mod;
}
int main() {
    #ifdef ONLINE_JUDGE
    #else
        freopen("a.in","r",stdin);
        // freopen("a.out","w",stdout);
    #endif
    n = read();
    FOR(i, 1, n) t[read()]++;
    jc[0] = 1;
    FOR(i, 1, n) jc[i] = 1ll * jc[i - 1] * i % mod;
    inv_jc[n] = ksm(jc[n], mod - 2);
    inv_jc[0] = 1;
    for(int i = n -1; i >= 1; --i) {
        inv_jc[i] = 1ll * inv_jc[i + 1] * (i + 1) % mod;
    }
    int sum = 0, ans = 1;
    FOR(i, 1, n) if(t[i]) {
        ans = 1ll * ans * jc[t[i]] % mod * C(sum + t[i] / 2, t[i] / 2) % mod;
        sum += t[i];
    }
    cout << ans;
    return 0;
}
posted @   小饼卷大葱  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示