peiwenjun's blog 没有知识的荒原

CF1392H ZS Shuffles Cards 题解

题目描述

\(n+m\) 张牌,其中 \(n\) 张标有数字 \(1,\cdots,n\) ,另外 \(m\) 张为鬼牌,初始随机打乱。

每轮你可以抽一张牌:

  • 抽到标有数字 \(x\) 的牌:从牌堆中移除这张牌,令 \(x\to S\)

  • 抽到鬼牌:

    如果 \(S=\{1,\cdots,n\}\) ,游戏结束。

    否则将 \(n+m\) 张牌随机打乱,重新开始抽牌。

求游戏结束的期望轮数,对 \(998244353\) 取模。

数据范围

  • \(1\le n,m\le 2\cdot 10^6\)

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\)

分析

\(f_{i,j}\) 表示牌堆中有 \(i\) 张未抽到的牌和 \(j\) 张已抽到的牌(都不算鬼牌),到达游戏结束的期望步数。

目标求出 \(f_{n,0}\)\(f_{i,j}\) 有三种转移:

  • 抽到一张未抽到的牌:转移到 \(f_{i-1,j}\) ,概率为 \(\frac i{i+j+m}\)
  • 抽到一张已抽到的牌:转移到 \(f_{i,j-1}\),概率为 \(\frac j{i+j+m}\)
  • 抽到鬼牌:转移到 \(f_{i,n-i}\) ,概率为 \(\frac m{i+j+m}\)

直接转移显然有后效性,考虑解方程。

先考虑 \(i=0\) 的情况,此时抽到鬼牌意味着游戏结束:

\[f_{0,0}=1,f_{0,j}=\frac j{j+m}(f_{0,j-1}+1)+\frac m{j+m}\\ \]

手玩一下就会发现:

\[f_{0,j}=\frac {j+m+1}{m+1}\\ \]


对于 \(i\neq 0\) 的情况,转移方程为:

\[f_{i,j}=\frac i{i+j+m}f_{i-1,j}+\frac j{i+j+m}f_{i,j-1}+\frac m{i+j+m}f_{i,n-i}+1\\ \tag 1 \]

\(j\gets j+1\)

\[f_{i,j+1}=\frac i{i+j+m+1}f_{i-1,j+1}+\frac {j+1}{i+j+m+1}f_{i,j}+\frac m{i+j+m+1}f_{i,n-i}+1\\ \tag 2 \]

\((i+j+m+1)\times(2)-(i+j+m)\times(1)\)

\[(i+j+m+1)f_{i,j+1}-(i+j+m)f_{i,j}=i\cdot f_{i-1,j+1}+(j+1)\cdot f_{i,j}-i\cdot f_{i-1,j}-j\cdot f_{i,j-1}+1\\ (i+j+m+1)(f_{i,j+1}-f_{i,j})=i\cdot (f_{i-1,j+1}-f_{i-1,j})+j\cdot (f_{i,j}-f_{i,j-1})+1\\ \]

\(g_{i,j}=f_{i,j+1}-f_{i,j}\)

\[(i+j+m+1)\cdot g_{i,j}=i\cdot g_{i-1,j}+j\cdot g_{i,j-1}+1\\ \]

注意到递推边界 \(g_{0,j}=\frac 1{m+1}\)因此 \(\forall i+j\le n\) ,均满足 \(g_{i,j}=\frac 1{m+1}\)

\((1)\) 中令 \(j=n-i\)

\[(n+m)f_{i,n-i}=i\cdot f_{i-1,n-i}+(n-i)\cdot f_{i,n-i-1}+m\cdot f_{i,n-i}+n+m\\ \]

\(f_{i-1,n-i}\) 替换成 \(f_{i-1,n-i+1}\) ,将 \(f_{i,n-i-1}\) 替换成 \(f_{i,n-i}\)

\[n\cdot f_{i,n-i}=i\cdot (f_{i-1,n-i+1}-\frac 1{m+1})+(n-i)\cdot (f_{i,n-i}-\frac 1{m+1})+n+m\\ i\cdot f_{i,n-i}=i\cdot f_{i-1,n-i+1}-\frac n{m+1}+n+m\\ f_{i,n-i}=f_{i-1,n-i+1}+\frac 1i(n+m-\frac n{m+1})\\ \]

结合初始值 \(f_{0,n}=\frac {n+m+1}{m+1}\) ,可知 \(f_{n,0}=\frac {n+m+1}{m+1}+(n+m-\frac n{m+1})\sum_{i=1}^n\frac 1i\)

时间复杂度可以做到 \(\mathcal O(n)\) ,但是快速幂求逆元也能过。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5,mod=998244353;
int m,n,inv,sum;
inline int qpow(int a,int k)
{
    int res=1;
    for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
    return res;
}
int main()
{
    scanf("%d%d",&n,&m),inv=qpow(m+1,mod-2);
    for(int i=1;i<=n;i++) sum=(sum+qpow(i,mod-2))%mod;
    printf("%lld\n",((n+m+1ll)*inv+(n+m-1ll*n*inv%mod+mod)*sum)%mod);
    return 0;
}

posted on 2023-06-19 09:28  peiwenjun  阅读(6)  评论(0)    收藏  举报

导航