【题解】LOJ #2325 「清华集训 2017」小 Y 和恐怖的奴隶主(矩阵快速幂)

【题解】LOJ #2325 「清华集训 2017」小 Y 和恐怖的奴隶主(矩阵快速幂)

\(f(i,a,b,c)\)表示进行到\(i\)次攻击,血量分别为\(1,2,3\)的怪的数量为\(a,b,c\)概率。如果你设表示攻击boss的次数期望的话就还要而外记录一个概率,不然无法转移,所以不如直接设成概率,每次转移\(f(i,a,b,c)\to f(i+1,a,b,c)\)的时候叠加一下答案即可(贡献为\(f(i,a,b,c)\over a+b+c+1\)),可以看做是\(ans(i)=ans(i-1)+\sum {f(i,a,b,c)\over a+b+c+1}\),和\(f\)一起dp即可。

状态数乍一看是\(8^3\),但是考虑到\(a+b+c\le 8\),也就是方程\(x_1+x_2+x_3+x_4=8\)的解的个数,解的数量是\({4+8-1\choose 8}=165\)

那么考虑矩阵快速幂就好了,注意到询问次数比较多,用向量优化的方法就行了。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;  typedef long long ll;
inline ll qr(){
	ll ret=0,f=0,c=getchar();
	while(!isdigit(c)) f|=c==45,c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return f?-ret:ret;
}
const int mod=998244353;
int MOD(const int&x){return x>=mod?x-mod:x;}
int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
int st[10][10][10],vec[maxn],temp[maxn],id;
//     1(a)2(b)3(c)

const int maxn=166;
struct{int x,y,z;}rem[maxn];
struct MAT{
	int v[maxn][maxn];
	MAT(){memset(v,0,sizeof(int)*maxn*maxn);}
	int* operator [](const int&x){return v[x];};
	MAT operator * (const MAT&x)const{
		MAT ret;
		for(int k=0;k<=id;++k)
			for(int t=0;t<=id;++t)
				for(int i=0;i<=id;++i)
					ret[t][i]=MOD(ret[t][i]+MOD(v[t][k],x.v[k][i]));
		return ret;
	}
}mi[61];
int ksm(const int&ba,const int&p){
	int ret=1;
	for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
		if(t&1) ret=MOD(ret,b);
	return ret;
}

int main(){
	int T=qr(),m=qr(),k=qr();
	for(int a=0;a<=k;++a)
		for(int b=0;(b+a<=k&&m>=2)||b==0;++b)
			for(int c=0;(a+b+c<=k&&m>=3)||c==0;++c)
				rem[id]={a,b,c},st[a][b][c]=id++;
	for(int t=0;t<id;++t){
		int a=rem[t].x,b=rem[t].y,c=rem[t].z,inv=ksm(a+b+c+1,mod-2);
		//hit boss
		mi[0][t][t]=inv;
		mi[0][t][id]=inv;
		//hit a (1 hp)
		if(a) mi[0][t][st[a-1][b][c]]=MOD(a,inv);		
		//hit b (2 hp)
		if(b) mi[0][t][st[a+1][b-1+(a+b<k&&m==2)][c+(a+b+c<k&&m==3)]]=MOD(b,inv);
		//hit c (3 hp)
		if(c) mi[0][t][st[a][b+1][c-1+(a+b+c<k)]]=MOD(c,inv);
	}
	mi[0][id][id]=1;
	for(int t=1;t<=60;++t) mi[t]=mi[t-1]*mi[t-1];	
	while(T--){
		memset(vec,0,sizeof vec);
		ll n=qr();
		vec[st[m==1][m==2][m==3]]=1;
		for(int t=0;t<=60;++t,n>>=1)
			if(n&1){
				memcpy(temp,vec,sizeof vec);
				memset(vec,0,sizeof vec);
				for(int j=0;j<=id;++j)
					for(int i=0;i<=id;++i)
						vec[i]=MOD(vec[i]+MOD(mi[t][j][i],temp[j]));
			}
		printf("%d\n",vec[id]);
	}
	return 0;
}


posted @ 2020-06-01 10:36  谁是鸽王  阅读(176)  评论(0编辑  收藏  举报