「CF1392H」ZS Shuffles Cards

题目

点这里 看题目。


你有 \(n+m\) 张牌,其中有恰好 \(n\) 张为数字牌,分别标有 \(1,2,3,\dots,n\),剩下的恰好 \(m\) 张均为鬼牌。

一开始,牌被随机打乱,同时你有一个集合 \(S=\varnothing\)。接下来,你将要进行如下操作若干轮:

一轮操作中,你需要从牌堆里选出一张牌:

  • 如果是数字牌,假设牌上标有数字 \(x\),则你需要将它从牌堆中取出放到一边,并令 \(S\gets S\cap \{x\}\)

  • 如果是鬼牌,则将所有的牌放回牌堆并重新洗牌,所以洗完牌后牌堆里仍然有 \(n+m\) 张牌。

    此时,如果 \(S=\{1,2,3,\dots,n\}\),操作结束。否则,\(S\) 不变,你需要继续抽牌。

\(X\) 为操作结束时经过的轮数,你需要求出 \(E[x]\bmod 998244353\)

所有数据满足 \(1\le n,m\le 2\times 10^6\)

分析

三年前不会做,三年后差点还是不会做。

\([n]=\{1,2,3,\dots,n\}\)。将操作过程看作:每次以一定概率取出一个集合 \(T\subseteq [n]\),并令 \(S\gets S\cap T\);如果某次操作完后 \(S=[n]\) 则停止,\(X\) 即为 \(\sum |T|\)

这个结构让人想到 min-max 反演。设 \(X_k\) 表示编号为 \(k\) 的牌第一次出现在集合 \(S\) 中时,已经出现过的 \(T\) 的大小之和,则 \(X=\max_{1\le k\le n}\{X_k\}\)。对它做反演,我们要求的就是 \(E[\min_{k\in A} X_k](A\subseteq [n],A\neq \varnothing)\)

答案显然只和 \(|A|\) 有关,所以设 \(s=|A|,E_s=E[\min_{k\in A}X_k]\)。于是,考察每一种大小的集合出现的概率:任意一种大小为 \(k\) 的集合的出现概率为 \(q_k=\frac{n^{\underline k}m}{(n+m)^{\underline{k+1}}}\),任意一种大小为 \(k\)不包含 \(A\) 中元素的集合的出现概率为 \(p_k=\frac{(n-s)^{\underline k}m}{(n+m)^{\underline{k+1}}}\)。另设 \(P=\sum_k p_k\),则可以得到:

\[E_s=\sum_{j\ge 0}\sum_k P^j((k+1)p_k+(k+1)(q_k-p_k))=\frac{1}{1-P}\sum_k (k+1)q_k \]

\(\sum_k (k+1)q_k\)\(s\) 无关,所以我们重点考虑求 \(P\)。不妨设 \(r=n-s\),那么有:

\[\begin{aligned} P &=\sum_{k=0}^r\frac{r^{\underline k}m}{(n+m)^{\underline{k+1}}}\\ &=\frac{m}{n+m}\sum_{k=0}^r\frac{\binom{r}{k}}{\binom{n+m-1}{k}}\\ &=\frac{m}{n+m}\sum_{k=0}^r\frac{\binom{n+m-1-k}{r-k}}{\binom{n+m-1}{r}}\\ &=\frac{m}{n+m}\binom{n+m-1}{r}^{-1}\sum_{k=0}^r\binom{n+m-1-k}{n+m-1-r}\\ &=\frac{m}{n+m}\binom{n+m-1}{r}^{-1}\binom{n+m}{n+m-r} \end{aligned} \]

于是就可以 \(O(1)\) 求出来了,于是就可以 \(O(n)\) 计算答案了。

进行后续数学推导也可以得到封闭结果,不过没有必要了。

代码

#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 998244353, inv2 = ( mod + 1 ) >> 1;
const int MAXN = 4e6 + 5;

template<typename _T>
inline void Read( _T &x ) {
	x = 0; char s = getchar(); bool f = false;
	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) Write( x / 10 );
	putchar( x % 10 + '0' );
}

int P[MAXN];
int fac[MAXN], ifac[MAXN], inv[MAXN];

int N, M;

inline int Qkpow( int, int );
inline int Inv( const int &a ) { return Qkpow( a, mod - 2 ); }
inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }

inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; } 

inline int S1( int n ) { return Mul( inv2, Mul( n, n + 1 ) ); }
inline int C( int n, int m ) { return n < m ? 0 : Mul( fac[n], Mul( ifac[m], ifac[n - m] ) ); }
inline int CInv( int n, int m ) { return n < m ? 0 : Mul( ifac[n], Mul( fac[m], fac[n - m] ) ); }

inline int Qkpow( int base, int indx ) {
	int ret = 1;
	while( indx ) {
		if( indx & 1 ) MulEq( ret, base );
		MulEq( base, base ), indx >>= 1;
	}
	return ret;
}

inline void Init( const int &n ) {
	fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
	ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
	rep( i, 1, n ) inv[i] = Mul( ifac[i], fac[i - 1] );
}

int main() {
	Read( N ), Read( M ), Init( N + M );
	int Q = 0, tmp;
	rep( x, 0, N ) {
		P[x] = Mul( Mul( M, inv[N + M] ), Mul( C( N + M, N + M - x ), CInv( N + M - 1, x ) ) );
		 tmp = Mul( Mul( M, inv[N + M] ), Mul( C( N, x ), CInv( N + M - 1, x ) ) );
		AddEq( Q, Mul( tmp, x + 1 ) );
	}
	int ans = 0;
	rep( s, 1, N ) {
		int f = Mul( Inv( Sub( 1, P[N - s] ) ), Q );
		s & 1 ? AddEq( ans, Mul( C( N, s ), f ) ) : SubEq( ans, Mul( C( N, s ), f ) );
	}
	Write( ans ), putchar( '\n' );
	return 0;
}
posted @ 2023-02-19 10:28  crashed  阅读(25)  评论(0编辑  收藏  举报