2021 ICPC Jinan C Optimal Strategy

在这里插入图片描述

题意:

题意很明确,n个值,两个人轮流取,问有多少种可能,然后取模。
这个题意看看样例应该很好理解

分析

首先统计一下每个数的个数,然后顺序排好,就没有问题了。
我推出的第一条性质是如果存在一个奇数个的大数,那么它一定会比这个数下面的数先被取走,偶数个数的大数,乃至偶数个的任何数都没有这个性质。
由此再考虑偶数个的数字,我们发现偶数取的规律十分随意,但是呢,有一个很抽象的性质,对于后手来说,先手如果取了一个偶数个数的数,那么我作为后手一定会去选择一个比你大或者等于你的偶数的数,这里的前提是没有个数的数了,因为前面说了,奇数一定在此之前就被取走了;(感性理解的话,这些数都是成对的,你拿啥我拿啥,甚至我拿的比你还大,那么两个人都用此策略,是完全不会影响答案的)理解之后发现规律,把对相同的偶数看作括号,则上面和下面的不能相交,也就是如果有3 ,3 ,4 ,4 四个数字,我如果拿3,你拿4我肯定不能拿3 了,这样就是相交,3,4,4,3就是包含,就是不相交的情况。其次呢,对于成对的大数,要么与小数并列,要么被小数的括号包含起来。
考虑这样的状态数,从小到大遍历,把成对的大数一起放,下面的小数是可以放进小数中间的,也就是放4,4 的时候,可以放成3,3,4,4或者4,4,3,3或者3,4 ,4,3;但是大的是在一起的,小数是可以分开的。
所以这样的话,我们考虑每个数往里放的贡献,累乘起来就好了。
这里挂出来官方题解:
在这里插入图片描述
首先ci!是肯定大家都知道的,然后每次后面的组合公式的意思是,对于每次往里放大数的时候,成对的放,就有ci/2取整对,然后考虑到放完之后会有在这里插入图片描述
种状态,那么从中选出ci/2种作为放入的序列,然后再乘上阶乘就是每次的答案,累乘起来就是结果。

#include <bits/stdc++.h>
#define ll long long
const ll mod = 998244353;
const ll p = 998244353;
const ll N = 2e6 + 1000;
using namespace std;
ll arr[N];
ll ux[N];
vector<ll> st;
typedef long long LL;
LL n, m;
LL quick_mod(LL a, LL b) {
    LL ans = 1;
    a %= p;
    while (b) {
        if (b & 1) {
            ans = ans * a % p;
            b--;
        }
        b >>= 1;
        a = a * a % p;
    }
    return ans;
}
LL C(LL n, LL m) {
    if (m > n) return 0;
    LL ans = 1;
    for (int i = 1; i <= m; i++) {
        LL a = (n + i - m) % p;
        LL b = i % p;
        ans = ans * (a * quick_mod(b, p - 2) % p) % p;
    }
    return ans;
}
LL Lucas(LL n, LL m) {
    if (m == 0) return 1;
    return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
inline void solve() {
    ux[0] = 1;
    for (ll i = 1; i <= 1000009; i++) {
        ux[i] = ux[i - 1] * i;
        ux[i] %= mod;
    }
    ll n;
    scanf("%lld", &n);
    for (ll i = 0; i < n; i++) {
        scanf("%lld", &arr[i]);
    }
    sort(arr, arr + n);
    ll cnt = 1;
    for (ll i = 1; i < n; i++) {
        if (arr[i] == arr[i - 1])
            cnt++;
        else
            st.push_back(cnt), cnt = 1;
    }
    st.push_back(cnt);
    ll t = 0, ans = 1;
    for (ll i = 0; i < st.size(); i++) {
        int uu=st[i]/2;
        ans*=Lucas(uu+t,uu);
        ans%=mod;
        ans*=ux[st[i]];ans%=mod;
        t+=st[i];
    }
    printf("%lld\n", ans % mod);
}
int main() {
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值