Live2D

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

Description

  Link.

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

Solution

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

f(i)=j=0if(j)t=2k/i+1g(ij,t).

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

g(i,j)=[i(j1)k]t=1ip(1p)t1(a>jg(t1,a))(bjg(it,b)).

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

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

f(i)=j=1k+1f(ij)h(j1,2)(i>k).

用 LSB-first, 暴力算乘法; 或者用 Fiduccia 都行. 复杂度 O(k2logk).

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 @   Rainybunny  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-08-15 Solution -「Gym 102759F」Interval Graph
2021-08-15 Solution -「Gym 102759C」Economic One-way Roads
点击右上角即可分享
微信分享提示