原题链接寄了,挂一个宣传
题目描述
题解
首先最优策略一定是手中的牌足够搞死Boss的时候再一发全打出去,并且顺序一定是毒药 → \rightarrow →火球 → \rightarrow →复读伤害最大。
所以我们有一个 O ( n 3 ) O(n^3) O(n3) 的暴力DP,因为三种牌任意一种超过 n n n 张的话都和恰好 n n n 张的效果没有区别,所以我们对边界处特殊计算,其它地方按照定义转移即可。
容易发现只有“复读”可能超过边界,所以只需要特殊处理一维。状态数虽然是 O ( n 3 ) O(n^3) O(n3),但是由于我们判断一个状态伤害足够的时候就会直接给DP值赋0,所以我们实际访问到的状态要少一些。在 n ≤ 300 n\le 300 n≤300 的时候,显然 n n n 最多询问300个值,此时我们用记忆化搜索,再把最大的50个值打个表就可以过前4个子任务。
然后说说正解。
期望可以转化成每一回合没有达到伤害的概率的和+1,也即拿到的每种长度的抽牌序列不满足条件的概率和+1。事实上由于抽到的牌就这几种组合,每种组合中每种牌的伤害在最优策略下固定,所以有更快的 O ( p o l y ( log n ) ) O(poly(\log n)) O(poly(logn)) 的做法:
讨论一下牌的几种组合:
- 只有复读,铁定赢不了: ∑ i = 1 + ∞ 1 3 i = 1 2 \sum_{i=1}^{+\infty}\frac{1}{3^i}=\frac{1}{2} ∑i=1+∞3i1=21;
- 只有毒药, n n n 张牌内赢不了: ∑ i = 1 n 1 3 i = 1 − 1 3 n 2 \sum_{i=1}^n\frac{1}{3^i}=\frac{1-\frac{1}{3^n}}{2} ∑i=1n3i1=21−3n1;
- 只有火球, ⌊ n − 1 2 ⌋ \lfloor\frac{n-1}{2}\rfloor ⌊2n−1⌋ 张牌内赢不了:设 m = ⌊ n − 1 2 ⌋ m=\lfloor\frac{n-1}{2}\rfloor m=⌊2n−1⌋,答案是 1 − 1 3 m 2 \frac{1-\frac{1}{3^m}}{2} 21−3m1;
- 火球+复读,伤害都是2,简单容斥一下: ∑ i = 1 m 2 i − 2 3 i = 2 ( 1 − ( 2 3 ) m ) − ( 1 − 1 3 m ) \sum_{i=1}^m\frac{2^i-2}{3^i}=2(1-(\frac{2}{3})^m)-(1-\frac{1}{3^m}) ∑i=1m3i2i−2=2(1−(32)m)−(1−3m1);
- 毒药+复读,这时候毒药(除去第一发)伤害为1,复读伤害为2,要求伤害<n的序列概率的和,相当于不考虑第一毒无伤的情况下伤害 ∈ [ 1 , n ] \in[1,n] ∈[1,n] 的序列的概率和。我们还是用容斥,这样就转变为了可以选毒药、复读或只有两者其一的概率和。这是一个小物品大容量的背包,可以用矩阵加速来求;
- 毒药+火球,毒药伤害1,火球伤害3,用类似5的容斥即可;
- 毒药+火球+复读,伤害分别为1、3和4,还是用类似5的容斥。
对于5,6,7三种情况的方便计算,你可以直接求出背包中是否包含1,2,3,4的 2 4 2^4 24 种情况的概率,然后容斥的时候直接调用其中几种进行计算即可。
代码
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
#pragma GCC optimize("Ofast")
using namespace std;
const int MAXN=305;
const ll INF=1e18;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
const ll MOD=998244353;
inline ll ksm(ll a,ll b,ll mo){
ll res=1;
for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
return res;
}
const ll iv3=ksm(3,MOD-2,MOD),iv2=(MOD+1)>>1;
ll n,m,ans,f[16];
int IN;
const int MN=10;
struct matrix{
ll c[MN][MN];int n,m;matrix(){}
matrix(int N,int M){memset(c,0,sizeof(c)),n=N,m=M;}
inline matrix operator*(const matrix&b)const{
matrix res=matrix(n,b.m);
for(int i=0;i<n;i++)
for(int k=0;k<m;k++)if(c[i][k])
for(int j=0;j<=b.m;j++)
res.c[i][j]=(res.c[i][j]+c[i][k]*b.c[k][j])%MOD;
return res;
}
};
inline matrix mpow(matrix a,ll b){
matrix res=matrix(a.n,a.n);
for(int i=0;i<a.n;i++)res.c[i][i]=1;
for(;b;b>>=1,a=a*a)if(b&1)res=res*a;
return res;
}
signed main()
{
freopen("stable.in","r",stdin);
freopen("stable.out","w",stdout);
for(int T=read();T--;){
n=read();
//1,2,3,4
ans=(2+MOD+2-ksm(iv3,n,MOD))*iv2%MOD;
ans=(ans+(MOD+1-ksm(iv3,(n-1)>>1,MOD))*iv2)%MOD;
ans=(ans+((MOD+1-ksm((iv3<<1)%MOD,(n-1)>>1,MOD))<<1)+MOD+ksm(iv3,(n-1)>>1,MOD)-1)%MOD;
matrix a,b;
for(int s=1;s<14;s++){
a=matrix(1,5),b=matrix(5,5);
a.c[0][3]=1;
if(s&1)a.c[0][4]=iv3;
b.c[0][0]=b.c[4][0]=b.c[2][1]=b.c[3][2]=b.c[4][3]=1;
for(int i=0;i<4;i++)if((s>>i)&1)b.c[4-i][4]=iv3;
a=a*mpow(b,n),f[s]=a.c[0][0];
}
//5
ans=(ans+f[3]-f[1]-f[2]+MOD+MOD)%MOD;
//6
ans=(ans+f[5]-f[1]-f[4]+MOD+MOD)%MOD;
//7
ans=((ans+f[13]-f[5]-f[12]-f[9]+f[1]+f[4]+f[8])%MOD+MOD)%MOD;
//over!
print(ans);
}
return 0;
}