• Solution -「洛谷 P4007」小 Y 和恐怖的奴隶主


    Description

    Link.

    这道题 的加强版。

    Solution

    题解里面大多数都是概率 DP,或者是期望 DP 然后是逆推。甚至不给 DP 的转移式。机房 yyds Reanap 发了一篇逆推的题解,那我就来补一篇正推的期望 DP 的填表法做法。

    首先这道题看上去好像可以状压的样子,我们可以设 (f_{i,S}) 表示当前打了 (i) 次,敌方的情况是 (S) 的期望。

    不过仔细想一下发现我们只需要知道各种血量的奴隶主有多少即可。

    于是我们重新设计 DP 的状态:(f_{s,i,j,k}) 表示目前打了 (s) 次,敌方分别有 (i)(j)(k) 个 1hp、2hp、3hp 的奴隶主。

    首先我们令 (T=[i+j+k<K])

    那么我们的方程就是:

    [f_{s,i,j,k}=egin{cases} f_{s-1,i-1,j,k}+frac{i}{i+j+k+1},M=1land i eq0 \ f_{s-1,i-1,j,k}+frac{i}{i+j+k+1},M=2land i eq0 \ f_{s-1,i+1,j-1+T,k}+frac{j}{i+j+k+1},M=2land j eq0 \ f_{s-1,i-1,j,k}+frac{i}{i+j+k+1},M=3land i eq0 \ f_{s-1,i+1,j-1,k+T}+frac{j}{i+j+k+1},M=3land j eq0 \ f_{s-1,i,j+1,k-1+T}+frac{k}{i+j+k+1},M=3land k eq0 end{cases} ]

    这个方程挺好理解的,基本就等于照题意模拟。

    然后我们发现转移式中的系数部分和 (f) 数组没有关系,所以我们可以用矩阵来加速这个东西。

    数一数状态数,直接加速直接 T 飞。

    有一个矩阵加速常用的 trick,预处理矩阵 2 的幂。

    然后取模卡卡常即可。

    (代码不保证稳定能过)

    #include <cstdio>
    #define mod ( 998244353 )
    
    using namespace std;
    typedef long long LL;
    
    char buf[1 << 21], *p1 = buf, *p2 = buf;
    #define getchar( ) ( p1 == p2 && ( p2 = ( p1 = buf ) + fread( buf, 1, 1 << 21, stdin ), p1 == p2 ) ? EOF : *p1 ++ )
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;
    	char c = getchar( );
    	_T f = 1;
    	while( c < '0' || c > '9' )
    	{
    		if( c == '-' )	f = -1;
    		c = getchar( );
    	}
    	while( c >= '0' && c <= '9' )	x = ( x << 3 ) + ( x << 1 ) + ( c ^ '0' ), c = getchar( );
    	x *= f;
    }
    
    template<typename _T, typename... Args>
    void read( _T &t, Args&... args ) { read( t ), read( args... ); }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 )	putchar( '-' ), x = -x;
    	if( x > 9 )	write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    void Add( _T &x, _T y )
    {
    	if( y >= mod )  y %= mod;
    	x += y;
    	if( x >= mod )  x -= mod;
    }
    
    template<typename _T>
    _T square( _T x ) { return x * x; }
    
    const int Maxn = 10 + 5, Maxk = 170 + 5;
    int T, M, K, S, Unite[Maxn][Maxn][Maxn];
    LL tmp[Maxk], Ans[Maxk], Inv[Maxn];
    struct Matrix
    {
    	LL mat[Maxk][Maxk];
    
    	friend Matrix operator * ( const Matrix &one, const Matrix &another )
    	{
    		Matrix res;
    		for( int i = 0; i <= S + 1; ++ i )
    		{
    			for( int j = 0; j <= S + 1; ++ j )
    			{
    				res.mat[i][j] = 0;
    				for( int k = 0; k <= S + 1; ++ k )	Add( res.mat[i][j], one.mat[i][k] * another.mat[k][j] );
    			}
    		}
    		return res;
    	}
    } dp[Maxk];
    
    template<typename _T>
    _T qkpow( _T base, _T times )
    {
    	_T res = 1;
    	while( times )
    	{
    		if( times & 1 )	res = ( LL )res * base % mod;
    		base = ( LL )base * base % mod;
    		times >>= 1;
    	}
    	return res;
    }
    
    void progressInversions( ) { for( int i = 0; i <= 10; ++ i )	Inv[i] = qkpow( i, mod - 2 ); }
    signed main( )
    {
    	progressInversions( );
    	read( T, M, K );
    	for( int i = 0; i <= K; ++ i )
    	{
    		int UpI;
    		if( M > 1 )	UpI = K - i;
    		else	UpI = 0;
    		for( int j = 0; j <= UpI; ++ j )
    		{
    			int UpJ;
    			if( M > 2 )	UpJ = K - i - j;
    			else	UpJ = 0;
    			for( int k = 0; k <= UpJ; ++ k )	Unite[i][j][k] = ++ S;
    		}
    	}
    	for( int i = 0; i <= K; ++ i )
    	{
    		int UpI;
    		if( M > 1 )	UpI = K - i;
    		else	UpI = 0;
    		for( int j = 0; j <= UpI; ++ j )
    		{
    			int UpJ;
    			if( M > 2 )	UpJ = K - i - j;
    			else	UpJ = 0;
    			for( int k = 0; k <= UpJ; ++ k )
    			{
    				int Add;
    				if( i + j + k < K )	Add = 1;
    				else	Add = 0;
    				if( M == 1 && i )	dp[0].mat[Unite[i][j][k]][Unite[i - 1][j][k]] = ( LL )i * Inv[i + j + k + 1] % mod;
    				else if( M == 2 )
    				{
    					if( i )	dp[0].mat[Unite[i][j][k]][Unite[i - 1][j][k]] = ( LL )i * Inv[i + j + k + 1] % mod;
    					if( j )	dp[0].mat[Unite[i][j][k]][Unite[i + 1][j - 1 + Add][k]] = ( LL )j * Inv[i + j + k + 1] % mod;
    				}
    				else if( M == 3 )
    				{
    					if( i )	dp[0].mat[Unite[i][j][k]][Unite[i - 1][j][k]] = ( LL )i * Inv[i + j + k + 1] % mod;
    					if( j )	dp[0].mat[Unite[i][j][k]][Unite[i + 1][j - 1][k + Add]] = ( LL )j * Inv[i + j + k + 1] % mod;
    					if( k )	dp[0].mat[Unite[i][j][k]][Unite[i][j + 1][k - 1 + Add]] = ( LL )k * Inv[i + j + k + 1] % mod;
    				}
    				dp[0].mat[Unite[i][j][k]][Unite[i][j][k]] = dp[0].mat[Unite[i][j][k]][S + 1] = Inv[i + j + k + 1];
    			}
    		}
    	}
    	dp[0].mat[S + 1][S + 1] = 1;
    	for( int i = 1; i <= 60; ++ i )	dp[i] = square( dp[i - 1] );
    	while( T -- > 0 )
    	{
    		LL N;
    		read( N );
    		for( int i = 0; i <= S + 1; ++ i )  Ans[i] = 0;
    		if( M == 1 )	Ans[Unite[1][0][0]] = 1;
    		else if( M == 2 )	Ans[Unite[0][1][0]] = 1;
    		else	Ans[Unite[0][0][1]] = 1;
    		for( int i = 0; i <= 60; ++ i )
    		{
    			if( ( N >> i ) & 1 )
    			{
    				for( int j = 0; j <= S + 1; ++ j )
    				{
    					tmp[j] = 0;
    					for( int k = 0; k <= S + 1; ++ k )	Add( tmp[j], Ans[k] * dp[i].mat[k][j] );
    				}
    				for( int j = 0; j <= S + 1; ++ j )	Ans[j] = tmp[j];
    			}
    		}
    		write( Ans[S + 1] ), putchar( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    Apache HTTP Server 与 Tomcat 的三种连接方式介绍(转)
    Java实现二叉树遍历以及常用算法
    随想-经验
    Java代码检查工具
    MongoDB学习笔记-维护
    脏检查
    html5对密码加密
    JavaSript模块化-AMD规范与CMD规范
    AngularJS的$watch用法
    常用的几个小函数
  • 原文地址:https://www.cnblogs.com/orchid-any/p/13750811.html
Copyright © 2020-2023  润新知