20211106 游戏 game
Description
给定两个人的血量 \(n\) , \(m\) 和攻击到对方的概率 \(p\) , \(q\) ,每次攻击到的话血量 \(-1\) ,回合制,问先手获胜的概率,对 998244353 取模。
多测。
\(\sum (n + m)\leq 5 \cdot 10 ^ 6 ,\ t \leq 10 ^ 4\)
Analysis
这好像看起来是一个比较良心的题??
实际上感觉还是很难操作的吧。。主要是我们并不清楚到底什么时候要结束。
容易发现对于每一个回合,只有四种情况。其中最影响我们的就是如果两个人都没有打到对方,因为这种情况我们不知道有多少个。
实际上的话本质上这玩意不影响我们的答案,因为四种情况都不相互影响,再加上对血量又没有影响,所以我们可以干脆不看这种情况。
Solution
接着 Analysis 的想法,然后我们设只打中先手的概率为 \(P_a\) ,只打中后手的概率为 \(P_b\) ,同时击中的概率为 \(P_c\) 。
要注意一下最后一个回合先手打死后手了,后手就没机会了,所以的话最后一次我们单独计算。剩下的就相当于我们就知道了 B 情况和 C 情况的总数,就是 \(m - 1\) 。
这样的话我们就可以枚举 C 情况的个数,那么很显然打中先手的数量要保证小于 \(n\) ,大概确定了多少个 A ,多少个 B ,多少个 C ,式子就是:
同时的话因为顺序不同就是另一种情况,而且每种情况内部又是无差别的,所以我们就再加上填法的系数,大概就是:
这玩意是 \(O(n ^ 2\log n)\) 的,显然不能过题,不过发现复杂度瓶颈就是后面那坨关于 \(P_a\) 的东西,这玩意可以预处理前缀和,包括前面的 \(i\) 次方也可以预处理,可以顺便把快速幂的 \(\log\) 去掉,那这下就是严格 \(O(n)\) 的了,可以通过此题。
Code
/*
3
1 1 50 50
100000 1 99 100
11 45 14 19
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e6 + 10;
const ll mod = 998244353;
ll n, m, p, _p, q, _q, ret;
ll pa[N], pb[N], pc[N], sum[N];
ll fac[N], inv[N], inv10, ans;
inline ll read() {
ll s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
return s * w;
}
inline ll ksm(ll a, ll b) {
ll tmp = 1;
while (b) {
if (b & 1) tmp = tmp * a % mod;
a = a * a % mod; b >>= 1;
}
return tmp;
}
inline ll C(int n, int m) {
if (n < m) return 0;
return (fac[n] * inv[m] % mod) * inv[n - m] % mod;
}
inline void mian() {
n = read(); m = read();
p = read(); _p = 100 - p;
q = read(); _q = 100 - q;
(p *= inv10) %= mod; (_p *= inv10) %= mod;
(q *= inv10) %= mod; (_q *= inv10) %= mod;
ll hv = ksm((1 - (_p * _q % mod) + mod), mod - 2);
pb[1] = (_q * p % mod) * hv % mod;
pa[1] = (_p * q % mod) * hv % mod;
sum[1] = (sum[0] + C(m, 1) * pa[1] % mod) % mod;
pc[1] = (q * p % mod) * hv % mod;
ans = hv * p % mod; ret = 0;
for (int i = 2; i <= max(n, m); ++i) {
pa[i] = pa[i - 1] * pa[1] % mod;
pb[i] = pb[i - 1] * pb[1] % mod;
pc[i] = pc[i - 1] * pc[1] % mod;
sum[i] = (sum[i - 1] + C(m + i - 1, i) * pa[i] % mod) % mod;
}
for (int i = 0; i < min(n, m); ++i) {
ll res = pc[i] * pb[m - 1 - i] % mod;
(ret += (res * C(m - 1, i) % mod) * sum[n - i - 1] % mod) %= mod;
}
printf("%lld\n", (ans + mod) * ret % mod);
}
int main() {
// freopen("game.in", "r", stdin);
// freopen("game.out", "w", stdout);
pa[0] = pb[0] = pc[0] = sum[0] = 1;
fac[0] = inv[0] = 1;
for (ll i = 1; i < N; ++i) {
fac[i] = fac[i - 1] * i % mod;
}
inv[N - 1] = ksm(fac[N - 1], mod - 2);
for (ll i = N - 2; i; --i) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
inv10 = ksm(100, mod - 2);
int t = read();
while (t--) mian();
return 0;
}