【2022省选模拟】[ZROI] 稳王——概率期望、矩阵加速

原题链接寄了,挂一个宣传

题目描述

在这里插入图片描述
在这里插入图片描述

题解

首先最优策略一定是手中的牌足够搞死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 n300 的时候,显然 n n n 最多询问300个值,此时我们用记忆化搜索,再把最大的50个值打个表就可以过前4个子任务。

然后说说正解。

期望可以转化成每一回合没有达到伤害的概率的和+1,也即拿到的每种长度的抽牌序列不满足条件的概率和+1。事实上由于抽到的牌就这几种组合,每种组合中每种牌的伤害在最优策略下固定,所以有更快的 O ( p o l y ( log ⁡ n ) ) O(poly(\log n)) O(poly(logn)) 的做法:

讨论一下牌的几种组合:

  1. 只有复读,铁定赢不了: ∑ i = 1 + ∞ 1 3 i = 1 2 \sum_{i=1}^{+\infty}\frac{1}{3^i}=\frac{1}{2} i=1+3i1=21
  2. 只有毒药, 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=213n1
  3. 只有火球, ⌊ n − 1 2 ⌋ \lfloor\frac{n-1}{2}\rfloor 2n1 张牌内赢不了:设 m = ⌊ n − 1 2 ⌋ m=\lfloor\frac{n-1}{2}\rfloor m=2n1,答案是 1 − 1 3 m 2 \frac{1-\frac{1}{3^m}}{2} 213m1
  4. 火球+复读,伤害都是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=1m3i2i2=2(1(32)m)(13m1)
  5. 毒药+复读,这时候毒药(除去第一发)伤害为1,复读伤害为2,要求伤害<n的序列概率的和,相当于不考虑第一毒无伤的情况下伤害 ∈ [ 1 , n ] \in[1,n] [1,n] 的序列的概率和。我们还是用容斥,这样就转变为了可以选毒药、复读或只有两者其一的概率和。这是一个小物品大容量的背包,可以用矩阵加速来求;
  6. 毒药+火球,毒药伤害1,火球伤害3,用类似5的容斥即可;
  7. 毒药+火球+复读,伤害分别为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值