P4007 小 Y 和恐怖的奴隶主

Link

期望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;
}
posted @ 2020-09-05 16:35  StarlightTobor  阅读(178)  评论(0)    收藏  举报