CF1156F Card Bag

计数 dp

题意

给定 \(n\) 张卡牌,每张牌上有一个数字 \(a_i\)

游戏进行不放回抽卡:从第二轮开始,比较当前抽到的数字 \(x\) 与上一轮的数字 \(y\)

\(x < y\) 则失败结束,若 \(x = y\) 则胜利结束,若 \(x > y\) 则继续。如果卡包抽空仍未胜利则失败。

求游戏胜利的概率,结果对 \(998244353\) 取模。

  • \(2 \leq n \leq 5000\)
  • \(1 \leq a_i \leq n\)

思路

考虑将操作转化为一个排列,胜利条件即为排列出现了相邻的两个元素相同且其之前的元素单调递增。

可以 dp 转移,最终答案就是胜利排列数除以总情况数 \(n!\)

由于胜利情况要求前缀单调递增且有两个数相等,因此后面可以随便填,不会出现重复(重复一定不合法)。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,ans;
int t[5010];
int f[5010][5010],g[5010][5010],sum[5010];
int fac[5010];
int ksm(int a,int b)
{
    if(b==0) return 1;
    int re=ksm(a,b>>1);
    re=re*re%mod;
    if(b&1) re=re*a%mod;
    return re;
}
signed main()   
{
    cin>>n; fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        t[x]++;
    }
    sum[0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            f[i][j]=sum[j-1]*t[i]%mod;
            if(j-2>=0) g[i][j]=sum[j-2]*t[i]%mod*(t[i]-1)%mod;
        }
        for(int j=0;j<=n;j++) sum[j]=(sum[j]+f[i][j])%mod;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            ans=(ans+g[i][j]*fac[n-j]%mod)%mod;
    cout<<ans*ksm(fac[n],mod-2)%mod<<endl;
    return 0;
}
posted @ 2026-01-27 21:41  crazy--boy  阅读(2)  评论(0)    收藏  举报