第九届河北省大学生程序设计竞赛 E题思路分享(dp,期望)

题意概述

需要连续击败 \(n\)\(boss\) 才能通关,计算通关的期望挑战次数。

具体规则如下:

  1. 初始血量为满血 \(h\),从头开始每成功打完 \(k\)\(boss\) 会有一个休息点,会在休息点回满血。

  2. 对于每一个 \(bos\)s,都有概率被 \(boss\) 秒杀,被秒杀的概率会受到掉血量的影响,满血时概率为 \(p_i\),每掉 \(1\) 滴血,这个概率会乘上一个倍数 \(x\)

  3. 如果被第 \(i\)\(boss\) 秒杀,那么本次挑战失败;否则,有 \(q_i\) 的概率受伤,受伤会掉 \(1\) 滴血,若血量变为 \(0\) 则也挑战失败。

  4. 每次挑战失败,都需要从头开始,成功打败第 \(n\)\(boss\) 并且血量不为 \(0\) 则挑战成功。

\(1 \leq n \leq 5 \times 10^4\), \(1 \leq h \leq 5 \times 10^4\), \(0 \leq k < n\), \(100 < x' \leq 200\)

特殊地,\(k = 0\) 时表示不存在休息点,保证通关概率不为 \(0\)

思路

记通关的概率为 \(P\),根据几何分布期望公式,通关的期望挑战次数为 \(1 / P\)

考虑 \(dp\),无法存所有血量状态,考虑优化第二维的血量状态。

注意到被秒杀的概率随掉血量增加指数增长,推测掉血量不能太多。

具体的,记当前掉血量为 \(j\),被秒杀概率需要小于 \(1\) ,即:

\[p_i \times x^j \lt 1 \]

解得:

\[j \lt \left\lceil \frac{-\ln p_i}{\ln x} \right\rceil \]

记这个上界为 \(mx_i\)

最坏的情况,\(x=1.01,p_i=0.01\),这样 \(mx_i\) 大概是 \(463\),第二维最多只需要开 \(500\)

得到这个性质之后, \(dp\) 转移即可。

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int MOD = 1e9+7;
const double eps = 1e-9;

ll qpow(ll a,ll b){
	ll res = 1;
	while (b){
		if (b&1){
			res = res*a%MOD;
		}

		a = a*a%MOD;
		b >>= 1;
	}

	return res;
}

void solve(){
	int n,h,k,x;
	cin >> n >> h >> k >> x;

	vector<int> p(n+1),q(n+1);
	for (int i=1;i<=n;i++){
		cin >> q[i];
	}
	for (int i=1;i<=n;i++){
		cin >> p[i];
	}

	vector<int> mx(n+1);
	for (int i=1;i<=n;i++){
		mx[i] = (int)ceil(-log(p[i]/100.0)/log(x/100.0)-eps);
	}

	int N = min(500,h);

	vector<vector<ll>> dp(n+1,vector<ll>(N,0));
	dp[0][0] = 1;
	ll temp = qpow(100,MOD-2);

	for (int i=1;i<=n;i++){
		for (int j=0;j<min(mx[i],N);j++){
			ll cur = dp[i-1][j]*((1-p[i]*temp%MOD*qpow(x*temp%MOD,j)%MOD+MOD)%MOD)%MOD;

			dp[i][j] = (dp[i][j]+(1-q[i]*temp%MOD+MOD)%MOD*cur%MOD)%MOD;

			if (j+1<N){
				dp[i][j+1] = (dp[i][j+1]+q[i]*temp%MOD*cur%MOD)%MOD;				
			}
		}

		if (k!=0 && i%k==0){
			for (int j=1;j<N;j++){
				dp[i][0] = (dp[i][0]+dp[i][j])%MOD;
				dp[i][j] = 0;	
			}	
		}
	}

	ll res = 0;
	for (int j=0;j<N;j++){
		res = (res+dp[n][j])%MOD;		
	}

	cout << qpow(res,MOD-2) << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-13 19:30  kzssCCC  阅读(11)  评论(0)    收藏  举报