1. 程式人生 > 實用技巧 >ZJOI2020 抽卡

ZJOI2020 抽卡

第一次出現合法狀態的期望時間可以轉換為所有非法狀態的出現概率乘以其在此處期望停留的時間。

設此非法狀態抽了 \(r\) 張卡,那麼其有 \(\binom{m}{r}^{-1}\) 的概率出現,經過 \(\frac{m}{m-r}\) 時間後會到達下一個狀態。

只需要計數所有非法狀態的數量即可。

對每個極長連續段考慮,設其長度為 \(n\),考慮其中選出 \(r\) 張牌的方案數應為:

\[[x^{n+1-r}]\bigg(\sum x^i[i<k]\bigg)^{r} \]

這一部分基於這樣的一個考慮,我們新增一個輔助元 \(n+1\) 並欽定其不被選中,這樣的話我們列舉沒有被選擇的元素的個數,併為其前面添增 \(0,1...k-1\)

個被選中的元素,這樣只需要將計算得到的答案反轉系數即可得到選中了 \(r\) 個元素的方案數。

因此,我們只需要考慮:

\[[x^{n+1-r}]\bigg(\sum x^i[i<k]\bigg)^{r} \]

接下來我們將結尾段的 \(0\) 加入計數的段落中,並新增輔助元 \(u\) 表示被選擇的 \(0\) 的數量,設 \(G(x)=\sum_{i\ge 1}x^i[i\le k]=x\frac{1-x^k}{1-x}\)

則我們希望求解的就是:

\[\sum (uG(x))^i=\frac{1}{1-uG(x)}[x^{n+1}] \]

\(H(x)=\frac{1}{1-ux}\)

,則所求為 \(H(G(x))[x^{n+1}]\),設 \(F(x)\)\(G(x)\) 的複合逆。

我們直接上拓展拉格朗日反演:

\[\frac{1}{1-uG(x)}[x^{n+1}]=\frac{1}{n+1}\frac{u}{(1-ux)^2}F(x)^{-(n+1)}[x^{-1}] \]

也即:

\[\frac{1}{n+1}\frac{u}{(1-ux)^2}\Big(\frac{x}{F(x)}\Big)^{(n+1)}[x^{n}] \]

事實上,由於 \(G(x)=\frac{x-x^{k+1}}{1-x}\),所以 \(F(x)\) 會滿足:

\[\frac{F(x)^{k+1}-F(x)}{F(x)-1}=x\to F(x)^{k+1}-(1+x)F(x)+x=0 \]

牛迭部分的方程如下:

\[F(x)=F_0(x)-\frac{x^{k}F_0(x)^{k+1}-(1+x)F_0(x)+1}{x^{k}(k+1)F_0(x)^{k}-(1+x)} \]

上面的 \(F(x)\)\(\frac{F(x)}{x}\)

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define re register
#define int long long
#define vi vector<int>
//視題目調節 
const int P = 998244353 ; 
const int Gi = 332748118 ;
const int G = 3 ; 
// 基本引數 
const int N = 2e6 + 5 ; 
int n, m, len, B[N], A[N], Dx[N] ; 
vector<int> f[N] ; 
int read() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = ( cn * 10 + cc - '0' ) % P, cc = getchar() ;
	return cn * flus ;
}
int fpow( int x, int k ) {
	int ans = 1, base = x ;
	while( k ) {
		if( k & 1 ) ans = ans * base % P ; 
		base = base * base % P, k >>= 1 ;
	} return ans % P ; 
}
void inc(int &x, int y) {
	((x += y) >= P) && (x -= P) ; 
}
void dec(int &x, int y) {
	((x -= y) < 0) && (x += P) ; 
}
namespace Poly {
	int limit, L, Inv, R[N], inv[N], gi[N << 1], igi[N << 1] ; 
	void Init( int x ) {
		limit = 1, L = 0 ; while( limit < x ) limit <<= 1, ++ L ;
		rep( i, 0, limit ) R[i] = ( R[i >> 1] >> 1 ) | ( ( i & 1 ) << ( L - 1 ) ) ; 
		Inv = fpow( limit, P - 2 ) ;
	}
	void _init( int x ) { // max x range
		for( re int i = 0; i <= x; ++ i ) inv[i] = fpow( i, P - 2 ) ;
		Init( x + x ) ; int num = 0 ;
		for( re int k = 1; k < limit; k <<= 1 ) {
			int d = fpow( G, ( P - 1 ) / ( k << 1 ) ) ;
			for( re int j = 0, g = 1; j < k; ++ j, g = g * d % P ) gi[++ num] = g ;
		} num = 0 ;
		for( re int k = 1; k < limit; k <<= 1 ) {
			int d = fpow( Gi, ( P - 1 ) / ( k << 1 ) ) ;
			for( re int j = 0, g = 1; j < k; ++ j, g = g * d % P ) igi[++ num] = g ;
		} 
	}
	void NTT( int *a, int type ) { //NTT多項式乘法 
		for( re int i = 0; i < limit; ++ i ) if( i < R[i] ) swap( a[i], a[R[i]] ) ;
		int num = 0 ;
		for( re int k = 1; k < limit; k <<= 1 ) {
			for( re int i = 0; i < limit; i += ( k << 1 ) )
			for( re int j = i, d = num + 1; j < i + k; ++ j, ++ d ) {
				int Nx = a[j], Ny = a[j + k] * ( ( type == 1 ) ? gi[d] : igi[d] ) % P ;
				a[j] = ( Nx + Ny ) % P, a[j + k] = ( Nx - Ny + P ) % P ; 
			} num += k ;
		} 
		if( type != 1 ) rep( i, 0, limit ) a[i] = a[i] * Inv % P ; 
	}
	
	void NTT( vi& a, int type ) { //NTT多項式乘法 
		for( re int i = 0; i < limit; ++ i ) if( i < R[i] ) swap( a[i], a[R[i]] ) ;
		int num = 0 ;
		for( re int k = 1; k < limit; k <<= 1 ) {
			for( re int i = 0; i < limit; i += ( k << 1 ) )
			for( re int j = i, d = num + 1; j < i + k; ++ j, ++ d ) {
				int Nx = a[j], Ny = a[j + k] * ( ( type == 1 ) ? gi[d] : igi[d] ) % P ;
				a[j] = ( Nx + Ny ) % P, a[j + k] = ( Nx - Ny + P ) % P ; 
			} num += k ;
		} 
		if( type != 1 ) rep( i, 0, limit ) a[i] = a[i] * Inv % P ; 
	}
	void Mul(vi& a, vi& b) {
		int l1 = a.size(), l2 = b.size() ; 
		Init(l1 + l2), a.resize(limit + 1), b.resize(limit + 1) ;
		NTT(a, 1), NTT(b, 1) ;
		rep( i, 0, limit ) a[i] = a[i] * b[i] % P ; 
		NTT(a, 0) ; int len = l1 + l2 ; 
		rep( i, len, limit ) a[i] = 0 ;
		a.resize(len) ;  
	}
	// 求逆 
	int H[N], Gx[N] ;
	void PolyInv( int *a, int x ) {
		Gx[0] = fpow( a[0], P - 2 ) ; int lim = 1 ; 
		while( lim < x ) {
			lim <<= 1, Init( lim << 1 ) ; 
			for( re int i = 0; i < lim; ++ i ) H[i] = a[i] ;
			NTT( H, 1 ), NTT( Gx, 1 ) ;
			for( re int i = 0; i < limit; ++ i ) Gx[i] = ( 2ll - Gx[i] * H[i] % P + P ) % P * Gx[i] % P ;
			NTT( Gx, 0 ) ; for( re int i = lim; i < limit; ++ i ) Gx[i] = 0 ;
		}
		for( re int i = 0; i <= x; ++ i ) a[i] = Gx[i] ;
		rep( i, 0, limit ) Gx[i] = H[i] = 0 ;
	} 
	void Deriv( int *a, int x ) { //求導 
		rep( i, 1, x ) a[i - 1] = a[i] * i % P ;
	}
	void Integ( int *a, int x ) { //積分 
		drep( i, 1, x ) a[i] = a[i - 1] * inv[i] % P ;
		a[0] = 0 ;
	}
	int Gt[N], F[N] ;
	void Polyln( int *a, int x ) {
		rep( i, 0, x ) F[i] = Gt[i] = a[i] ;
		PolyInv( Gt, x ), Deriv( F, x ) ; 
		Init( x << 1 ), NTT( Gt, 1 ), NTT( F, 1 ) ;
		rep( i, 0, limit ) F[i] = F[i] * Gt[i] % P ;
		NTT( F, 0 ), Integ( F, x ) ;
		rep( i, 0, x - 1 ) a[i] = F[i] ;
		rep( i, 0, limit ) F[i] = Gt[i] = 0 ;
	}
	int _Gt[N], _F[N], _H[N] ; 
	void Polyexp( int *a, int x ) {
		_Gt[0] = 1 ; int lim = 1 ; 
		while( lim < x ) {
			lim <<= 1 ; rep( i, 0, lim - 1 ) _F[i] = a[i], _H[i] = _Gt[i] ;
			Polyln( _H, lim ), Init( lim << 1 ) ;
			NTT( _Gt, 1 ), NTT( _H, 1 ), NTT( _F, 1 ) ;
			rep( i, 0, limit ) _Gt[i] = ( 1ll - _H[i] + _F[i] + P ) % P * _Gt[i] % P ;
			NTT( _Gt, 0 ) ; rep( i, lim, limit ) _Gt[i] = 0 ;
		}
		rep( i, 0, x - 1 ) a[i] = _Gt[i] ;
		rep( i, 0, limit ) _F[i] = _H[i] = _Gt[i] = 0 ;
	}
	void PolyKSM( int *a, int x, int k ) {
		Polyln( a, x ) ;
		rep( i, 0, x - 1 ) a[i] = a[i] * k % P ;
		Polyexp( a, x ) ;
	}
	int _Nw[N], _Gx[N], _Gy[N], _Gz[N], _Go[N], _GG[N] ; 
	void Newton(int *a, int x) {
		_Nw[0] = 1 ; int lim = 1 ; 
		while( lim < x ) {
			lim <<= 1 ; rep( i, 0, lim - 1 ) _Gz[i] = _Gx[i] = _Gy[i] = _Nw[i] ; 
			PolyKSM(_Gx, lim, m) ; 
			rep( i, 0, lim ) _GG[i] = _Gx[i] ; 
			Init(lim << 1) ;
			NTT(_GG, 1), NTT(_Gy, 1) ;
			rep( i, 0, limit ) _Gy[i] = _Gy[i] * _GG[i] % P ; 
			NTT(_Gy, 0) ; 
			rep( i, lim, limit ) _Gy[i] = 0 ; 
			rep( i, 0, lim ) _Gx[i] = _Gx[i] * (m + 1) % P ; 
			rep( i, 0, lim ) _Go[i] = _Gx[i], _Gx[i] = 0 ; 
			rep( i, m, lim - 1 ) _Gx[i] = _Go[i - m] ; 
			dec(_Gx[0], 1), dec(_Gx[1], 1), PolyInv(_Gx, lim) ; 
			for(re int i = lim - 1; i; -- i) 
			inc( _Gz[i], _Gz[i - 1] ) ;
			rep( i, 0, lim ) _Go[i] = _Gy[i], _Gy[i] = 0 ; 
			rep( i, m, lim - 1 ) _Gy[i] = _Go[i - m] ; 
			rep( i, 0, lim ) dec(_Gy[i], _Gz[i]) ;
			inc(_Gy[0], 1), Init(lim << 1) ; 
			NTT(_Gx, 1), NTT(_Gy, 1) ; 
			rep( i, 0, limit ) _Gx[i] = _Gx[i] * _Gy[i] % P ; 
			NTT(_Gx, 0) ; 
			rep( i, lim, limit ) _Gx[i] = 0 ; 
			rep( i, 0, lim - 1 ) dec(_Nw[i], _Gx[i]) ; 
		}
		rep( i, 0, x - 1 ) a[i] = _Nw[i] ; 
	} 
}
int top, st[N], fac2[N], inv2[N] ; 
void solve(int l, int r) {
	if(l == r) return ; 
	int mid = (l + r) >> 1 ; 
	solve(l, mid), solve(mid + 1, r) ; 
	Poly::Mul(f[l], f[mid + 1]) ; 
}
int C(int x, int y) {
	return 1ll * fac2[x] * inv2[y] % P * inv2[x - y] % P ; 
}
signed main()
{
	n = read(), m = read(), fac2[0] = inv2[0] = 1 ; 
	rep( i, 1, n ) B[i] = read() ;
	rep( i, 1, n ) fac2[i] = fac2[i - 1] * i % P ;
	rep( i, 0, n ) inv2[i] = fpow(fac2[i], P - 2) ; 
	Poly::_init(n * 2), Poly::Newton(A, n) ;
	Poly::PolyInv(A, n) ; 
	sort(B + 1, B + n + 1), B[0] = -1 ; int l = 0 ; 
	rep( i, 1, n ) {
		if(B[i] != B[i - 1] + 1) st[++ top] = l, l = 0 ; ++ l ; 
	} st[++ top] = l ; 
	for(re int i = 1; i <= top; ++ i) {
		int u = st[i], iv = fpow(u + 1, P - 2) ; 
		f[i].resize(u + 1) ;
		rep( j, 0, u ) Dx[j] = A[j] ; 
		Poly::PolyKSM(Dx, u + 1, u + 1) ;
		rep( j, 1, u ) f[i][j] = j * Dx[u - j + 1] % P * iv % P ; 
		rep( j, 1, u ) if((u - j + 1) > j) swap(f[i][j], f[i][u - j + 1]) ; 
		f[i][0] = 1 ; 
	} 
	solve(1, top) ; 
	int Ans = 0 ; 
	rep( i, 1, n - 1 ) Ans = (Ans + f[1][i] * fpow(C(n, i), P - 2) % P * n % P * fpow(n - i, P - 2)) % P ; 
	cout << (Ans + 1) % P << endl ; 
	return 0 ;
}