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

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 i i i-th card is a i a_i ai.

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 x x x), you compare it with the card that was removed during the previous turn (let the number on it be y y y). Possible outcomes are:

if x &lt; y x&lt;y x<y, the game ends and you lose;
if x = y x=y x=y, the game ends and you win;
if x &gt; y x&gt;y x>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 P Q \frac{P}{Q} QP where P and Q are non-negative integers and Q   ! = 0 , P ≤ Q Q\ != 0, P\leq Q Q !=0,PQ. Output the value of P ⋅ Q − 1 P⋅Q^{−1} PQ1 ( m o d   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 n n n integers a 1 , a 2 , … a n ( 1 ≤ a i ≤ n ) a_1,a_2,…a_n (1≤a_i≤n) a1,a2,an(1ain) — the i i i-th integer is the number written on the i i i-th card.

Output

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

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 1 10 \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 1 4 \frac{1}{4} 41.

题意:

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

解法:

  • 统计每个数的个数,从小到大依次记为 b 1 , b 2 . . . b l e n b_1,b_2...b_{len} b1,b2...blen其中 l e n len len为数的种数,且 b 1 &lt; b 2 &lt; . . . &lt; b l e n b_1&lt;b_2&lt;...&lt;b_{len} b1<b2<...<blen,对应的个数分别为 c n t 1 , c n t 2 . . . c n t l e n cnt_1,cnt_2...cnt_{len} cnt1,cnt2...cntlen,然后从左往右扫一遍,若当前的 c n t i ≥ 2 cnt_i\geq2 cnti2则可能对答案构成贡献,计算这个贡献就要用排列组合了
  • 首先从 c n t i cnt_i cnti中选出两个数并排列,作为最后取的两个,方案为 A c n t i 2 A_{cnt_i}^{2} Acnti2
  • 设当前枚举的是 b b b中的第 i i i个,然后考虑这两个数的前面放多少个数(显然前面的这些数小于当前枚举的数,而且只能递增放置,每个数最多放一个),枚举个数(假设为 j j j个),那么问题是怎么知道从比 b i b_i bi小的所有数中选出 j − 1 j-1 j1个满足上述条件的方案数呢?这是可以用一个背包dp[i][j]表示从前i个选出j个不同的数的方案数,那么有如下转移方程:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ c n t i dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*cnt_i dp[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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值