P4007 小 Y 和恐怖的奴隶主
期望dp,\(f_{i,a,b,c}\) 表示攻击第 \(i\) 次后状态为(\(a\) 个 1 血,\(b\) 个 2 血,\(c\) 个 3 血随从) 的期望扣血数
然后我发现这玩意正着推没法算答案,也就是 \(f_i\) 从 \(f_{i-1}\) 转移,而且初始状态有点难搞
有一句话叫做:
概率是顺推,而期望需要逆推
那么我们考虑倒着转移:\(inv_i\) 表示 \(i\) 在 \(\bmod 998244353\) 意义下的逆元
\(f_{i,a,b,c}\leftarrow f_{i,a,b,c}+(f_{i+1,a,b,c}+1)\times inv_{a+b+c+1} \bmod 998244353\)
+1 是因为多扣了一滴血,然后后面的逆元是因为发生这个事件的概率是 \(\dfrac {1}{a+b+c+1}\)
然后下面的也就差不多了,举个攻击 1 滴血随从的例子
\(f_{i,a,b,c} \leftarrow f_{i,a,b,c}+f_{i+1,a-1,b,c}\times a\times inv_{a+b+c+1} \bmod 998244353\)
攻击 \(i+1\) 次后比攻击 \(i\) 次后少了一个 1 血随从,所以是 \(a-1\),然后发生的概率是 \(\dfrac {a}{a+b+c+1}\) ,
其余的类似。然后合法的 \(a,b,c\) 状态不会超过 170,可以状态压缩一下,然后由于 \(f_i\) 从 \(f_{i+1}\) 转移,可以使用矩阵快速幂优化。
\(\texttt{Code:}\)
#include <bits/stdc++.h>
#define reg register
#define int long long
#define mem(x,y) memset(x,y,sizeof x)
#define ln puts("")
using namespace std;
const int p=998244353;
struct Status
{
int x,y,z;
};
template <class t> inline void read(t &s){
s=0;reg int f=1;reg char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))s=(s<<3)+(s<<1)+(c^48),c=getchar();s*=f;return;}
template<class t,class ...A> inline void read(t &x,A &...a){read(x);read(a...);return;}
template <class t> inline void write(t x){if(x<0)putchar('-'),x=-x;int buf[21],top=0;
while(x)buf[++top]=x%10,x/=10;if(!top)buf[++top]=0;while(top)putchar(buf[top--]^'0');
return;}
inline void add(int &x,int y)
{
(x+=y)>=p&&(x-=p);
return;
}
int idx[9][9][9],sum[170],idxcnt;
struct Mat
{
int a[170][170];
inline void clear()
{
mem(a,0);
return;
}
inline Mat operator * (const Mat &nt) const
{
Mat res;res.clear();
for(int k=1;k<=idxcnt+1;++k)
for(int i=1;i<=idxcnt+1;++i)
for(int j=1;j<=idxcnt+1;++j)
add(res.a[i][j],a[i][k]*nt.a[k][j]%p);
return res;
}
}base;
struct Vector
{
int a[170];
inline void clear()
{
mem(a,0);
return;
}
inline Vector operator * (const Mat &nt) const
{
reg Vector res;res.clear();
for(int i=1;i<=idxcnt+1;++i)
for(int j=1;j<=idxcnt+1;++j)
add(res.a[j],a[i]*nt.a[i][j]%p);
return res;
}
}I;
int m,K,n;
Status rev[170];
Mat pre[64];
int inv[10];
inline int fastpow(int a,int b,int p)
{
reg int res=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
res=res*a%p;
return res;
}
signed main(void)
{
int t;
read(t,m,K);
n=100;
for(int i=1;i<10;++i)
inv[i]=fastpow(i,p-2,p);
for(int i=0;i<=K;++i)
for(int j=0;j<=(m>=2?K-i:0);++j)
for(int k=0;k<=(m>=3?K-i-j:0);++k)
idx[i][j][k]=++idxcnt,sum[idxcnt]=i+j+k,rev[idxcnt]=(Status){i,j,k};
for(int i=1;i<=idxcnt;++i)
{
base.a[i][i]=inv[sum[i]+1];
base.a[idxcnt+1][i]=inv[sum[i]+1];
reg int ns=0;
if(rev[i].x)
{
ns=idx[rev[i].x-1][rev[i].y][rev[i].z];
base.a[ns][i]=rev[i].x*inv[sum[i]+1]%p;
}
if(rev[i].y)
{
reg int X=rev[i].x+1,Y=rev[i].y-1,Z=rev[i].z;
if(sum[i]<K)
switch(m)
{
case 1:++X;break;
case 2:++Y;break;
case 3:++Z;break;
}
ns=idx[X][Y][Z];
base.a[ns][i]=rev[i].y*inv[sum[i]+1]%p;
}
if(rev[i].z)
{
reg int X=rev[i].x,Y=rev[i].y+1,Z=rev[i].z-1;
if(sum[i]<K)
switch(m)
{
case 1:++X;break;
case 2:++Y;break;
case 3:++Z;break;
}
ns=idx[X][Y][Z];
base.a[ns][i]=rev[i].z*inv[sum[i]+1]%p;
}
}
base.a[idxcnt+1][idxcnt+1]=1;
pre[0]=base;
for(int i=1;i<=62;++i)
pre[i]=pre[i-1]*pre[i-1];
I.clear();
I.a[idxcnt+1]=1;
while(t--)
{
read(n);
Vector Ans=I;
for(int i=0;i<=60;++i)
if(n&(1ll<<i))
Ans=Ans*pre[i];
write(Ans.a[idx[m==1][m==2][m==3]]),ln;
}
return 0;
}

浙公网安备 33010602011771号