• 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;
    }
    
    
  • 相关阅读:
    最小生成树(二)Prim算法
    红黑树
    最短路径:Dijstra(迪杰斯特拉)算法
    30岁的程序员,未来之路-写于疫情笼罩下的北京-中国-地球
    Spring Cloud 系列之Hystrix、Ribbon、Feign 源码剖析(一)引子
    A left join B B表有多条记录,max(create_time)取最新一条
    最新idea破解码,亲测可用
    Spring Cloud Gateway简单使用
    开源的C#实现WebSocket协议客户端和服务器websocket-sharp组件解析
    简单易用的.NET免费开源RabbitMQ操作组件EasyNetQ解析
  • 原文地址:https://www.cnblogs.com/rainybunny/p/16587774.html
Copyright © 2020-2023  润新知