题解
其实这题很容易想到状压dp,设 d p i , S dp_{i,S} dpi,S为当前还有 i i i点伤害需要释放,并且敌人目前战况为 S S S时对Boss造成伤害的期望值
n n n特别大怎么办?矩阵快速幂!
根据数学推导,设 t o t tot tot为战况的总数,则 t o t ≤ 166 tot\le166 tot≤166,复杂度 O ( T t o t 3 log n ) O(Ttot^3\log n) O(Ttot3logn)
等等!这样不会超时吗?
考虑做一个小优化:首先,矩阵乘法的复杂度其实是 O ( n m k ) O(nmk) O(nmk),不一定是 O ( t o t 3 ) O(tot^3) O(tot3)
而一个方阵乘一个向量是 O ( t o t 2 ) O(tot^2) O(tot2)的
所以可以利用这个特性,预处理矩阵的 2 i 2^i 2i次幂,每次查询时找出需要的那些矩阵即可
类似的方法可用于矩阵快速幂的题目中查询特别多的情况,复杂度 O ( t o t 3 log n + T t o t 2 log n ) O(tot^3\log n+Ttot^2\log n) O(tot3logn+Ttot2logn)
这题特别卡常(我最后一个点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;
}