「CF1156F」Card Bag【背包+排列组合】

本文介绍了一种游戏中的概率计算方法,通过统计每个数的出现次数,使用排列组合计算玩家获胜的概率。详细解析了如何通过动态规划解决这一问题,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

F. Card Bag

time limit per test: 2 seconds
memory limit per test: 512 megabytes
input: standard input
output: standard output

You have a bag which contains n cards. There is a number written on each card; the number on iii-th card is aia_iai.

You are playing the following game. During each turn, you choose and remove a random card from the bag (all cards that are still left inside the bag are chosen equiprobably). Nothing else happens during the first turn — but during the next turns, after removing a card (let the number on it be xxx), you compare it with the card that was removed during the previous turn (let the number on it be yyy). Possible outcomes are:

if x&lt;yx&lt;yx<y, the game ends and you lose;
if x=yx=yx=y, the game ends and you win;
if x&gt;yx&gt;yx>y, the game continues.
If there are no cards left in the bag, you lose. Cards are not returned into the bag after you remove them.

You have to calculate the probability of winning in this game. It can be shown that it is in the form of PQ\frac{P}{Q}QP where P and Q are non-negative integers and Q !=0,P≤QQ\ != 0, P\leq QQ !=0,PQ. Output the value of P⋅Q−1P⋅Q^{−1}PQ1 (mod 998244353)(mod\ 998244353)(mod 998244353).

Input

The first line contains one integer n(2≤n≤5000)n (2\leq n\leq5000)n(2n5000) — the number of cards in the bag.

The second live contains nnn integers a1,a2,…an(1≤ai≤n)a_1,a_2,…a_n (1≤a_i≤n)a1,a2,an(1ain) — the iii-th integer is the number written on the iii-th card.

Output

Print one integer — the probability of winning in this game modulo 998244353998244353998244353.

Examples

input
5
1 1 4 2 3
output
299473306
input
2
2 2
output
1
input
5
4 5 1 3 2
output
0
input
4
1 3 4 3
output
748683265

Note

In the first test case the probability of winning is 110\frac{1}{10}101.

In the second test case the probability of winning is 1.

In the third test case the probability of winning is 0.

In the fourth test case the probability of winning is 14\frac{1}{4}41.

题意:

  • 包里面有nnn个数,每次取一个数,当前取出的数为xxx,前一个取出的数为yyy,则:
    • x&gt;yx&gt;yx>y,游戏继续
    • x=yx=yx=y,游戏结束并且玩家赢
    • x&lt;yx&lt;yx<y,游戏结束并且玩家输
      问玩家赢的概率

解法:

  • 统计每个数的个数,从小到大依次记为b1,b2...blenb_1,b_2...b_{len}b1,b2...blen其中lenlenlen为数的种数,且b1&lt;b2&lt;...&lt;blenb_1&lt;b_2&lt;...&lt;b_{len}b1<b2<...<blen,对应的个数分别为cnt1,cnt2...cntlencnt_1,cnt_2...cnt_{len}cnt1,cnt2...cntlen,然后从左往右扫一遍,若当前的cnti≥2cnt_i\geq2cnti2则可能对答案构成贡献,计算这个贡献就要用排列组合了
  • 首先从cnticnt_icnti中选出两个数并排列,作为最后取的两个,方案为Acnti2A_{cnt_i}^{2}Acnti2
  • 设当前枚举的是bbb中的第iii个,然后考虑这两个数的前面放多少个数(显然前面的这些数小于当前枚举的数,而且只能递增放置,每个数最多放一个),枚举个数(假设为jjj个),那么问题是怎么知道从比bib_ibi小的所有数中选出j−1j-1j1个满足上述条件的方案数呢?这是可以用一个背包dp[i][j]表示从前i个选出j个不同的数的方案数,那么有如下转移方程:
    dp[i][j]=dp[i−1][j]+dp[i−1][j−1]∗cntidp[i][j]=dp[i-1][j]+dp[i-1][j-1]*cnt_idp[i][j]=dp[i1][j]+dp[i1][j1]cnti
  • 然后把剩下的没有选上的统统放到后面就行了,(当然这些数要排列一下)
    上述三个结果想乘就是当前位构成的贡献,然后把所有的相加就是总贡献,除以n!n!n!就是赢的概率

附代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll mod = 998244353;

int n, a[5005], cnt[5005];
int num[5005], len = 0;
ll fac[5005];
ll dp[5005][5005];

ll quick_pow(ll a, ll b)
{
    ll res = 1ll;
    while(b){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        cnt[a[i]]++;
    }
    fac[0] = 1ll;
    for(int i = 1; i <= 5004; i++) fac[i] = fac[i - 1] * i % mod;
    for(int i = 1; i <= 5004; i++) if(cnt[i]){
            num[++len] = i;
            cnt[len] = cnt[i];
    }
    ll ans = 0ll;
    for(int i = 0; i <= len; i++) dp[i][0] = 1ll;
    for(int i = 1; i <= len; i++){
        for(int j = 1; j <= i; j++){
            dp[i][j] = (dp[i - 1][j - 1] * cnt[i] + dp[i - 1][j]) % mod;
        }
    }
    for(int i = 1; i <= len; i++){
        if(cnt[i] >= 2){
            for(int j = i; j >= 1; j--){
                ans = (ans + ((dp[i - 1][j - 1] * cnt[i] * (cnt[i] - 1)) % mod * fac[n - 2 - j + 1] % mod)) % mod;
            }
        }
    }
    ans = ans * quick_pow(fac[n], mod - 2) % mod;
    printf("%lld\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值