其他分享
首页 > 其他分享> > 2021 icpc 济南站C Optimal Strategy

2021 icpc 济南站C Optimal Strategy

作者:互联网

problem

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

solution

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

sum是1到i-1的ti的和,\(t[i] = \sum_{1}^{n} [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;
}

标签:int,sum,Strategy,1ll,2021,Optimal,jc,inv,mod
来源: https://www.cnblogs.com/acmnb/p/16559818.html