【清华集训2017】【LOJ2325】【洛谷P4007】小 Y 和恐怖的奴隶主(矩阵快速幂)

LOJ传送门

洛谷传送门


解析:

显然 m m m这么小不是拿来看看完事的。

我们发现真正影响当前状态的只有每种血量的小怪的个数。

打表 m = 3 , K = 8 m=3,K=8 m=3,K=8发现合法的情况只有165种。

所以我们只需要给每个状态一个编号就可以把矩阵压到 165 165 165

每种状态 i , j , k i,j,k i,j,k(三种血量的分别有 i , j , k i,j,k i,j,k个)对答案的贡献是 P ( i , j , k ) / ( i + j + k + 1 ) P(i,j,k)/(i+j+k+1) P(i,j,k)/(i+j+k+1) P P P表示达到这个状态的概率。

矩阵再开最后一行记录期望前缀和(即答案)。

暴力预处理所有状态到其他状态的转移系数矩阵,以及2的若干次幂的矩阵。

然后每个询问就可以 O ( 16 6 2 log ⁡ n ) O(166^2\log n) O(1662logn)回答了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline ll getint(){
		re char c;
		while(!isdigit(c=gc()));re ll num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

using std::cout;
using std::cerr;

cs int mod=998244353;
inline int add(int a,int b){return (a+b>=mod)?a+b-mod:a+b;}
inline void Inc(int &a,int b){(a+=b)>=mod?a-=mod:a;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int quickpow(int a,int b,int res=1){
	while(b){
		if(b&1)res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}

int T,m,K;
int id1[9],id2[9][9],id3[9][9][9];

int SIZE;
inline void dfs(int cur,int tmp){
	static int rec[4];
	if(cur>m)switch(m){
		case 1:id1[rec[1]]=SIZE++;return ;
		case 2:id2[rec[1]][rec[2]]=SIZE++;return ;
		case 3:id3[rec[1]][rec[2]][rec[3]]=SIZE++;return ;
	}
	for(int re i=0;i<=tmp;++i)rec[cur]=i,dfs(cur+1,tmp-i);
}

struct matrix{
static cs int Mat=167;
	int a[Mat][Mat],r,c;
	matrix(){}
	matrix(int _r,int _c):r(_r),c(_c){memset(a,0,sizeof a);}
	
	int *operator[](int offset){return a[offset];}
	cs int *operator[](int offset)cs{return a[offset];}
	
	friend matrix operator*(cs matrix &A,cs matrix &B){
		matrix C(A.r,B.c);
		for(int re i=0;i<A.r;++i)
		for(int re j=0;j<B.r;++j)if(A[i][j])
		for(int re k=0;k<B.c;++k)
		Inc(C[i][k],mul(A[i][j],B[j][k]));
		return C; 
	}
}P[64],Ans;

inline void init_1(){
	for(int re i=0;i<=K;++i){
		int inv=quickpow(i+1,mod-2);
		P[0][id1[i]][id1[i]]=inv;
		if(i)P[0][id1[i]][id1[i-1]]=mul(inv,i);
		P[0][id1[i]][SIZE]=inv; 
	}
	P[0][SIZE][SIZE]=1;P[0].r=SIZE+1,P[0].c=SIZE+1;
}

inline void init_2(){
	for(int re i=0;i<=K;++i)for(int re j=0;j+i<=K;++j){
		int inv=quickpow(i+j+1,mod-2);
		P[0][id2[i][j]][id2[i][j]]=inv;
		if(i)P[0][id2[i][j]][id2[i-1][j]]=mul(inv,i);
		if(j)P[0][id2[i][j]][id2[i+1][j-1+(i+j<K)]]=mul(inv,j);
		P[0][id2[i][j]][SIZE]=inv;
	}
	P[0][SIZE][SIZE]=1;P[0].r=SIZE+1,P[0].c=SIZE+1;
}

inline void init_3(){
	for(int re i=0;i<=K;++i)for(int re j=0;i+j<=K;++j)for(int re k=0;i+j+k<=K;++k){
		int inv=quickpow(i+j+k+1,mod-2);
		P[0][id3[i][j][k]][id3[i][j][k]]=inv;
		if(i)P[0][id3[i][j][k]][id3[i-1][j][k]]=mul(inv,i);
		if(j)P[0][id3[i][j][k]][id3[i+1][j-1][k+(i+j+k<K)]]=mul(inv,j);
		if(k)P[0][id3[i][j][k]][id3[i][j+1][k-1+(i+j+k<K)]]=mul(inv,k);
		P[0][id3[i][j][k]][SIZE]=inv;
	}
	P[0][SIZE][SIZE]=1;P[0].r=SIZE+1,P[0].c=SIZE+1; 
}

signed main(){
	T=getint(),m=getint(),K=getint();
	dfs(1,K);
	switch(m){
		case 1:init_1();break;
		case 2:init_2();break;
		case 3:init_3();break;
	}
	for(int re i=1;i<64;++i)P[i]=P[i-1]*P[i-1];
	while(T--){
		ll n=getint();
		Ans=matrix(1,SIZE+1);
		switch(m){
			case 1:Ans[0][id1[1]]=1;break;
			case 2:Ans[0][id2[0][1]]=1;break;
			case 3:Ans[0][id3[0][0][1]]=1;break;
		}
		for(int re i=63;~i;--i)if(n>>i&1)Ans=Ans*P[i];
		cout<<Ans[0][SIZE]<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值