§ 题意简述
这道题 的加强版。
§ 题解
题解里面大多数都是概率 DP,或者是期望 DP 然后是逆推。甚至不给 DP 的转移式。机房 yyds Reanap 发了一篇逆推的题解,那我就来补一篇正推的期望 DP 的填表法做法。
首先这道题看上去好像可以状压的样子,我们可以设 表示当前打了 次,敌方的情况是 的期望。
不过仔细想一下发现我们只需要知道各种血量的奴隶主有多少即可。
于是我们重新设计 DP 的状态: 表示目前打了 次,敌方分别有 、、 个 1hp、2hp、3hp 的奴隶主。
首先我们令
那么我们的方程就是:
这个方程挺好理解的,基本就等于照题意模拟。
然后我们发现转移式中的系数部分和 数组没有关系,所以我们可以用矩阵来加速这个东西。
数一数状态数,直接加速直接 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( '\n' );
}
return 0;
}