CF1392 H. ZS Shuffles Cards

题目叙述

\(n\) 张正常的牌(不同的)和 \(m\) 张鬼牌,每次可以抽出一张牌,如果是鬼牌则将所有抽到的牌都放回去。如果是正常的牌则收下。问期望多少次能够将所有的正常牌都抽一遍。

题解

方法一

\(f_{i,j}\) 表示现在抽到过 \(i\) 张正常的牌,桌子上有 \(j\) 张牌和手里的重合,期望还需要多少次。
转移是容易的:

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

考虑差分。\(f_{i,j}-f_{i+1,j}\) 会得到一个算式。然后可以发现这是一个关于他们差分的递推式。
而我们发现 \(f_{0}{i}\) 构成一个等差数列。根据这个关于 \(f_{i,j}\) 的递推式,可以发现 \(f_{i,j}\)\(f_{i,j+1}\) 的差也是一个固定的数,而且就是 \(\frac{1}{m+1}\)
就此就可以通过 \(f_{i,0}\) 推出 \(f_{i,i}\) 的值。
再根据 \(f_{i,j}\) 的递推式算出 \(f_{i-1,0}\)\(f_{i,0}\) 的递推,就可以算出答案了。

方法二

可以发现两次抽出鬼牌之间的期望次数是相等的,都是 \(\frac{n+m}{m}\) 次。
所以其实只需要求出最后一次抽出的鬼牌是第几张抽出的即可。
\(f_i\) 表示第 \(i\) 张牌抽出之后第一张不是鬼牌的牌是抽出的第几次抽出鬼牌。
所以我们要求的就是 \(E(\max f_i)\)
考虑 \(\min -\max\) 容斥,\(E(\max f_i)=\sum_{\emptyset \not=s\subseteq U}(-1)^{|s|+1}E(\min_{j\in s}f_j)\)
其中 \(U\) 为所有牌组成的集合。
\(E(\min_{i\in s} f_i)\) 的含义就是在抽出这一张牌之前,抽出的鬼牌数量+1。
可以发现这时候抽出一张需要的牌的概率是固定的,即 \(\frac{|s|}{|s|+m}\) ,因此期望步数就是 \(\frac{|s|+m}{|s|}\) 。再套上那个算式计算一下就好了,具体就不细说了。

总结

  • 可以先猜测一些二维递推比较简单之类的。这个就每行都是一个等差数列。
  • 另外对于这种递推式可以尝试差分一下。会有一些效果。
  • 如果达成一件事情的概率在变化(比如这题里因为牌数变化的原因导致的问题),那么可以考虑 \(\min-\max\) 容斥把变化的转化为不变的。

代码

#include <cstdio>
#include <iostream>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l);i<=(r);++i)
#define ROF(i,r,l) for(int i=(r);i>=(l);--i)
using namespace std;
typedef long long LL;
const int Mod=998244353;
int add(int &x,int y){return ((x+=y)>=Mod)?(x-=Mod):x;}
int dec(int &x,int y){return ((x-=y)<0)?(x+=Mod):x;}
int ad(int x,int y){return ((x+y)>=Mod)?(x+y-Mod):(x+y);}
int dc(int x,int y){return ((x-y)<0)?(x-y+Mod):(x-y);}
int ml(int x,int y){return (LL)x*y%Mod;}
int ksm(int x,int y){
	int ret=1;
	for(;y;y>>=1,x=ml(x,x))if(y&1)ret=ml(ret,x);
	return ret;
}
int N,M;
int main(){
	// freopen("h.in","r",stdin);
	// freopen(".out","w",stdout);
	scanf("%d%d",&N,&M);
	int ans=0;
	for(int i=0;i<=N-1;++i){
		int tmp=ad(ml(M,N),ad(ml(M,M),ad(N-i,M)));
		add(ans,ml(tmp,ksm(ml(M+1,N-i),Mod-2)));
	}
	printf("%d\n",ad(ans,1));
	// fclose(stdin);
	// fclose(stdout);
	return 0;
}
posted @ 2022-06-30 16:10  YouthRhythm  阅读(11)  评论(0编辑  收藏  举报