Live2D

Solution -「NOI 2017」「洛谷 P3824」泳池

\(\mathscr{Description}\)

  Link.

  给定 \(n,k,p\), 求在一个 \(\infty\times n\) 的矩阵中, 每个位置的值以 \(p\) 的概率为 \(0\), \(1-p\) 的概率为 \(1\) 时, 以第一行为边界的最大全 \(0\) 子矩阵大小恰好为 \(k\) 的概率. 答案模 \(998244353\).

\(\mathscr{Solution}\)

  把 \(k\) 容斥成 \(\le\). 令 \(f(i)\) 表示考虑了前 \(i\) 列, 所有合法子矩阵 \(\le k\) 的概率, \(g(i,j)\) 表示一个 \(i\) 列的矩阵, 第一个 \(1\) 出现在第 \(j\) 行, 且 \(\infty\times i\) 里不存在非法子矩阵的概率. 那么

\[f(i)=\sum_{j=0}^if(j)\sum_{t=2}^{k/i+1}g(i-j,t). \]

  先来考察 \(g\). 枚举第 \(j\) 行上第一个 \(1\):

\[g(i,j)=[i(j-1)\le k]\sum_{t=1}^ip(1-p)^{t-1}\left(\sum_{a>j}g(t-1,a)\right)\left(\sum_{b\ge j}g(i-t,b)\right). \]

处理 \(g(i,j)\) 关于 \(j\) 的后缀和 \(h(i,j)\), 不难做到 \(\mathcal O(k^2\log k)\) 转移. 当然可以用多项式算法优化.

  此后, 我们得到了一个常系数齐次线性递推:

\[f(i)=\sum_{j=1}^{k+1}f(i-j)h(j-1,2)\quad(i>k). \]

用 LSB-first, 暴力算乘法; 或者用 Fiduccia 都行. 复杂度 \(\mathcal O(k^2\log k)\).

\(\mathscr{Code}\)

/* Clearink */

#include <cstdio>
#include <cstring>

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

const int MAXK = 1000, MOD = 998244353;
int n, m, p, pwr[MAXK + 5];
int f[MAXK + 5][MAXK + 5], A[MAXK * 2 + 5], F[MAXK * 2 + 5];

inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += MOD ); }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int mpow( int a, int b ) {
	int ret = 1;
	for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
	return ret;
}

inline int dp( const int i, const int j, const int m ) {
	if ( !j ) return 1;
	if ( i * j > m ) return 0;
	int& cur = f[i][j];
	if ( ~cur ) return cur;
	cur = mul( pwr[j], dp( i + 1, j, m ) );
	rep ( k, 1, j ) {
		addeq( cur, mul( mul( pwr[k - 1], sub( 1, p ) ),
			mul( dp( i + 1, k - 1, m ), dp( i, j - k, m ) ) ) );
	}
	return cur;
}

inline void polyMul( const int n, const int m, int& r,
	const int* u, const int* v, int* w ) {
	static int tmp[MAXK * 2 + 5];
	rep ( i, 0, n + m ) tmp[i] = 0;
	rep ( i, 0, n ) rep ( j, 0, m ) addeq( tmp[i + j], mul( u[i], v[j] ) );
	r = n + m;
	rep ( i, 0, r ) w[i] = tmp[i];
}

inline void polyMod( int& n, const int m, int* u, const int* v ) {
	per ( i, n, m ) {
		if ( !u[i] ) continue;
		int coe = u[i];
		rep ( j, 0, m ) subeq( u[i - j], mul( coe, v[m - j] ) );
	}
	n = n < m - 1 ? n : m - 1;
	while ( n && !u[n] ) --n;
}

inline int calc( int n, const int m ) {
	static int Q[MAXK * 2 + 5], S[MAXK * 2 + 5], G[MAXK * 2 + 5];

	memset( Q, 0, sizeof Q );
	memset( S, 0, sizeof S );
	memset( G, 0, sizeof G );

	int lq = m, ls = 0, lg = 1;
	Q[m] = 1;
	rep ( i, 0, m - 1 ) Q[m - i - 1] = sub( 0, A[i] );
	S[0] = G[1] = 1;

	for ( ; n; n >>= 1 ) {
		if ( n & 1 ) {
			polyMul( ls, lg, ls, S, G, S );
			polyMod( ls, lq, S, Q );
		}
		polyMul( lg, lg, lg, G, G, G );
		polyMod( lg, lq, G, Q );
	}

	int ret = 0;
	rep ( i, 0, m - 1 ) addeq( ret, mul( F[i], S[i] ) );
	return ret;
}

inline int solve( int m ) {
	memset( f, 0xff, sizeof f );
	dp( 0, m, m );

	memset( F, 0, sizeof F ), memset( A, 0, sizeof A );
	F[0] = 1, A[0] = sub( 1, p );
	rep ( i, 1, m ) {
		F[i] = f[0][i];
		A[i] = mul( f[1][i], mul( sub( 1, p ), pwr[i] ) );
	}

	return calc( n, m + 1 );
}

int main() {
	int x, y;
	scanf( "%d %d %d %d", &n, &m, &x, &y ), p = mul( x, mpow( y, MOD - 2 ) );

	pwr[0] = 1;
	rep ( i, 1, m ) pwr[i] = mul( pwr[i - 1], p );
	
	printf( "%d\n", sub( solve( m ), solve( m - 1 ) ) );
	return 0;
}

posted @ 2022-08-15 11:47  Rainybunny  阅读(78)  评论(0)    收藏  举报