小 Y 和恐怖的奴隸主 題解

傳送門

題解

其實這題很容易想到狀壓dp,設dpi,Sdp_{i,S}爲當前還有ii點傷害需要釋放,並且敵人目前戰況爲SS時對Boss造成傷害的期望值

nn特別大怎麼辦?矩陣快速冪!

根據數學推導,設tottot爲戰況的總數,則tot166tot\le166,複雜度O(Ttot3logn)O(Ttot^3\log n)

等等!這樣不會超時嗎?

考慮做一個小優化:首先,矩陣乘法的複雜度其實是O(nmk)O(nmk),不一定是O(tot3)O(tot^3)

而一個方陣乘一個向量是O(tot2)O(tot^2)

所以可以利用這個特性,預處理矩陣的2i2^i次冪,每次查詢時找出需要的那些矩陣即可

類似的方法可用於矩陣快速冪的題目中查詢特別多的情況,複雜度O(tot3logn+Ttot2logn)O(tot^3\log n+Ttot^2\log n)

這題特別卡常(我最後一個點TLE了好幾次),具體實現時可以參照我的代碼中的一些小優化

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll p=998244353,P=(0x7fffffffffffffffll/p-p)*p,N=170;
ll q,n,m,k,m1,x[20],y[20],first;
ll a[N];
void upd(ll &a,ll b){a+=b;if(P<=a)a-=P;}//卡常優化:開大模數
ll hx(){
	ll ans=0;
	for(ll i=1;i<=m1;i++)(ans<<=1)|=x[i];
	return ans;
}
void xh(ll h){
	for(ll i=m1;i;i--)x[i]=h&1,h>>=1;
}
ll expo(ll a,ll b){
	ll c=1;
	while(b){
		if(b&1)(c*=a)%=p;
		(a*=a)%=p;
		b>>=1;
	}
	return c;
}
ll inv(ll a){
	return expo(a,p-2);
}
void dfs(ll l,ll t){
	if(m1<l&&t==m)a[++n]=hx();
	if(m1<l)return;
	x[l]=0,dfs(l+1,t);
	if(t<m)x[l]=1,dfs(l+1,t+1);
}
ll find(ll x){
	return lower_bound(a+1,a+n+1,x)-a;
}
struct mat{
	ll a[N][N];
	mat mul(mat &b){
		mat ans;
		for(ll i=0;i<=n;i++)for(ll j=0;j<=n;j++)ans.a[i][j]=0;
		for(ll i=0;i<=n;i++)for(ll k=0;k<=n;k++){
			if(!b.a[i][k])continue;//卡常優化:由於矩陣是一個稀疏矩陣,所以可以直接跳過0
			for(ll j=0;j<=n;j++)upd(ans.a[i][j],a[k][j]*b.a[i][k]);
		}
		for(ll i=0;i<=n;i++)for(ll j=0;j<=n;j++)ans.a[i][j]%=p;
		return ans;
	}
}ma,init[70];
struct vec{
	ll a[N];
	vec mul(mat &b){
		vec ans;
		for(ll i=0;i<=n;i++){
			ans.a[i]=0;
			for(ll k=0;k<=n;k++)upd(ans.a[i],a[k]*b.a[i][k]);
		}
		for(ll i=0;i<=n;i++)ans.a[i]%=p;
		return ans;
	}
}fs,ans;
int main(){
	scanf("%lld%lld%lld",&q,&m,&k),m1=m+k;
	dfs(1,0),ma.a[0][0]=fs.a[0]=1;
	for(ll i1=1;i1<=n;i1++){
		xh(a[i1]);
		ll flag=1;
		for(ll i=2;i<=m+1;i++)if(!x[i])flag=0;
		if(flag)first=i1;
		for(ll i=1;i<=m1;i++)y[i]=x[i];
		ll tot=1;
		for(ll i=1,j=m;j;j-=y[i++])tot+=y[i]^1;
		tot=inv(tot);
		ma.a[i1][0]=ma.a[i1][i1]=tot;
		for(ll i=2;i<=m1;i++)if(y[i]&&(!y[i-1])){
			ll tot2=0;
			for(ll j=i-1;j&&!y[j];j--)tot2++;
			for(ll j=1;j<=m1;j++)x[j]=y[j];
			swap(x[i-1],x[i]);
			ll flag=0;
			for(ll j=i+1;j<=m1;j++)if(y[j])flag=1;
			if(flag&&!y[m1]){for(ll j=m1;j!=1;j--)x[j]=x[j-1];x[1]=0;}
			ma.a[i1][lower_bound(a+1,a+n+1,hx())-a]=tot*tot2%p;
		}
	}
	init[0]=ma;
	for(ll i=1;i<61;i++)init[i]=init[i-1].mul(init[i-1]);
	while(q--){
		ans=fs;
		ll a,i=0;scanf("%lld",&a);
		while(a){
			if(a&1)ans=ans.mul(init[i]);++i;
			a>>=1;
		}
		printf("%lld\n",ans.a[first]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章