题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=7331
题意
玩 n n n 次游戏,每次赢的概率都是 a b \dfrac{a}{b} ba,当你赢的时候,如果这是你第 k k k 次赢,则获得分数 k m k^m km。求:获得分数的期望,对 998244353 998244353 998244353 取模。
数据范围: 1 ≤ m ≤ 1 0 6 , 1 ≤ n , a , b < 998244353 1 \le m \le 10^6,1\le n,a,b < 998244353 1≤m≤106,1≤n,a,b<998244353。
做法
关键词:第二类斯特林数性质、二项式系数性质、幂级数求导/积分
官方题解看不太懂,机房大佬想出了大力推式子做法,为了透彻理解再手打一遍:
令
p
=
a
b
p=\dfrac{a}{b}
p=ba,显然有:
a
n
s
=
∑
i
=
0
n
(
n
i
)
p
i
(
1
−
p
)
n
−
i
∑
j
=
0
i
j
m
ans=\sum_{i=0}^n\binom{n}{i}p^i(1-p)^{n-i}\sum_{j=0}^ij^m
ans=i=0∑n(in)pi(1−p)n−ij=0∑ijm
令
q
=
p
1
−
p
q=\dfrac{p}{1-p}
q=1−pp:
a
n
s
=
(
1
−
p
)
n
∑
i
=
0
n
(
n
i
)
q
i
∑
j
=
0
i
j
m
ans=(1-p)^n\sum_{i=0}^n\binom{n}{i}q^i\sum_{j=0}^ij^m
ans=(1−p)ni=0∑n(in)qij=0∑ijm
因为
j
m
j^m
jm 很棘手,考虑斯特林数的性质:
x
m
=
∑
k
=
0
m
{
m
k
}
x
k
‾
x^m=\sum_{k=0}^m {m \brace k}x^{\underline{k}}
xm=k=0∑m{km}xk
代入:
a
n
s
=
(
1
−
p
)
n
∑
i
=
0
n
(
n
i
)
q
i
∑
j
=
0
i
∑
k
=
0
m
{
m
k
}
j
k
‾
ans=(1-p)^n\sum_{i=0}^n\binom{n}{i}q^i\sum_{j=0}^i\sum_{k=0}^m{m \brace k}j^{\underline{k}}
ans=(1−p)ni=0∑n(in)qij=0∑ik=0∑m{km}jk
交换求和顺序:
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
∑
i
=
0
n
(
n
i
)
q
i
∑
j
=
0
i
j
k
‾
ans=(1-p)^n\sum_{k=0}^m{m \brace k}\sum_{i=0}^n\binom{n}{i}q^i\sum_{j=0}^ij^{\underline{k}}
ans=(1−p)nk=0∑m{km}i=0∑n(in)qij=0∑ijk
拆掉下降幂:
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
k
!
∑
i
=
0
n
(
n
i
)
q
i
∑
j
=
0
i
(
j
k
)
ans=(1-p)^n\sum_{k=0}^m{m \brace k}k!\sum_{i=0}^n\binom{n}{i}q^i\sum_{j=0}^i\binom{j}{k}
ans=(1−p)nk=0∑m{km}k!i=0∑n(in)qij=0∑i(kj)
由二项式系数的上指标求和:
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
k
!
∑
i
=
0
n
(
n
i
)
q
i
(
i
+
1
k
+
1
)
ans=(1-p)^n\sum_{k=0}^m{m \brace k}k!\sum_{i=0}^n\binom{n}{i}q^i\binom{i+1}{k+1}
ans=(1−p)nk=0∑m{km}k!i=0∑n(in)qi(k+1i+1)
注意到有二项式系数相乘的结构,为了凑出熟悉的
(
r
m
)
(
m
k
)
=
(
r
k
)
(
r
−
k
m
−
k
)
\binom{r}{m}\binom{m}{k}=\binom{r}{k}\binom{r-k}{m-k}
(mr)(km)=(kr)(m−kr−k),自然想到吸收恒等式:
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
k
!
k
+
1
∑
i
=
0
n
(
n
i
)
(
i
k
)
(
i
+
1
)
q
i
ans=(1-p)^n\sum_{k=0}^m{m \brace k}\dfrac{k!}{k+1}\sum_{i=0}^n\binom{n}{i}\binom{i}{k}(i+1)q^i
ans=(1−p)nk=0∑m{km}k+1k!i=0∑n(in)(ki)(i+1)qi
然后利用上面的结论得到:
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
k
!
k
+
1
(
n
k
)
∑
i
=
0
n
(
n
−
k
i
−
k
)
(
i
+
1
)
q
i
ans=(1-p)^n\sum_{k=0}^m{m \brace k}\dfrac{k!}{k+1}\binom{n}{k}\sum_{i=0}^n\binom{n-k}{i-k}(i+1)q^i
ans=(1−p)nk=0∑m{km}k+1k!(kn)i=0∑n(i−kn−k)(i+1)qi
外面的阶乘和组合数化成下降幂:
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
n
k
‾
k
+
1
∑
i
=
0
n
(
n
−
k
i
−
k
)
(
i
+
1
)
q
i
ans=(1-p)^n\sum_{k=0}^m{m \brace k}\dfrac{n^{\underline{k}}}{k+1}\sum_{i=0}^n\binom{n-k}{i-k}(i+1)q^i
ans=(1−p)nk=0∑m{km}k+1nki=0∑n(i−kn−k)(i+1)qi
里面的那坨东西还不能直接用二项式定理化简,因为出现了
x
i
(
i
+
1
)
x^i(i+1)
xi(i+1) 的结构,那自然想到和积分/求导有关系:
f
k
(
x
)
=
∑
i
=
0
n
(
n
−
k
i
−
k
)
(
i
+
1
)
x
i
∫
f
k
(
x
)
d
x
=
∑
i
=
0
n
(
n
−
k
i
−
k
)
x
i
+
1
+
C
=
x
k
+
1
∑
j
=
0
n
−
k
(
n
−
k
j
)
x
j
+
C
=
x
k
+
1
(
1
+
x
)
n
−
k
+
C
f
k
(
x
)
=
x
k
(
1
+
x
)
n
−
k
−
1
[
(
k
+
1
)
(
1
+
x
)
+
x
(
n
−
k
)
]
\begin{aligned} f_k(x)&=\sum_{i=0}^n\binom{n-k}{i-k}(i+1)x^i \\ \int f_k(x)dx&=\sum_{i=0}^n\binom{n-k}{i-k}x^{i+1}+C \\ &=x^{k+1}\sum_{j=0}^{n-k}\binom{n-k}{j}x^j+C \\ &=x^{k+1}(1+x)^{n-k}+C \\ f_k(x)&=x^k(1+x)^{n-k-1}[(k+1)(1+x)+x(n-k)] \end{aligned}
fk(x)∫fk(x)dxfk(x)=i=0∑n(i−kn−k)(i+1)xi=i=0∑n(i−kn−k)xi+1+C=xk+1j=0∑n−k(jn−k)xj+C=xk+1(1+x)n−k+C=xk(1+x)n−k−1[(k+1)(1+x)+x(n−k)]
这样就求出了
f
k
(
x
)
f_k(x)
fk(x) 的封闭形式,从而有
a
n
s
=
(
1
−
p
)
n
∑
k
=
0
m
{
m
k
}
n
k
‾
k
+
1
f
k
(
q
)
ans=(1-p)^n\sum_{k=0}^m{m \brace k}\dfrac{n^{\underline{k}}}{k+1}f_k(q)
ans=(1−p)nk=0∑m{km}k+1nkfk(q)
预处理第二类斯特林数·行,快速幂计算
f
k
(
x
)
f_k(x)
fk(x),复杂度
O
(
m
(
log
m
+
log
n
)
)
O(m(\log m+\log n))
O(m(logm+logn))。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXM = 1e6+5;
using ll = long long;
using poly = vector<ll>;
const ll mod = 998244353;
vector<int> rev;
ll fac[MAXM], facinv[MAXM];
ll qpow(ll a, ll b, ll p)
{
if(b < 0)
{
ll tmp = qpow(a, -b, p);
return qpow(tmp, p-2, p);
}
ll ret = 1; a %= p;
while(b)
{
if(b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
void NTT(poly& f, int opt)
{
for(int i = 0; i < f.size(); ++i) if(i < rev[i]) swap(f[i], f[rev[i]]);
for(int mid = 1; mid < f.size(); mid <<= 1)
{
ll gn = qpow(opt==1?3:332748118, (mod-1)/(mid<<1), mod);
for(int j = 0; j < f.size(); j += (mid<<1))
{
ll w = 1;
for(int k = 0; k < mid; ++k, w = w * gn % mod)
{
ll x = f[j+k], y = w * f[j+k+mid] % mod;
f[j+k] = (x+y) % mod;
f[j+k+mid] = (x-y+mod) % mod;
}
}
}
if(opt == -1)
{
ll deginv = qpow(f.size(), mod-2, mod);
for(int i = 0; i < f.size(); ++i) f[i] = f[i] * deginv % mod;
}
}
poly poly_linear(poly f, poly g, int len, function<ll(ll,ll)> func)
{
int c = 0; for(int t = 1; t < len; t <<= 1) ++c;
int deg = (1 << c); rev.resize(deg); rev[0] = 0;
for(int i = 1; i < deg; ++i) rev[i] = (rev[i>>1]>>1)+((i&1)<<(c-1));
f.resize(deg); g.resize(deg); NTT(f, 1); NTT(g, 1);
for(int i = 0; i < deg; ++i) f[i] = func(f[i], g[i]);
NTT(f, -1); f.resize(len);
return f;
}
poly operator*(const poly& a, const poly& b)
{
return poly_linear(a,b,a.size()+b.size()-1,[](ll a, ll b){return a*b%mod;});
}
void pre()
{
fac[0] = 1;
for(int i = 1; i <= 1000001; ++i) fac[i] = fac[i-1] * i % mod;
facinv[1000001] = qpow(fac[1000001], mod-2, mod);
for(int i = 1000000; i >= 0; --i) facinv[i] = facinv[i+1] * (i+1) % mod;
}
int n,m;
ll h(ll k, ll x)
{
ll a = qpow(x, k, mod) * qpow(1+x, n-k-1, mod) % mod;
ll b = ((k+1) * (1+x) % mod + x * (n-k) % mod) % mod;
return (a * b % mod + mod) % mod;
}
int main()
{
cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);
pre();
int T; cin >> T;
while(T--)
{
int a,b; cin >> n >> m >> a >> b;
poly f(m+1), g(m+1);
ll p = a * qpow(b, mod-2, mod) % mod;
ll ompinv = qpow(1-p+mod, mod-2, mod);
ll q = p * ompinv % mod;
for(int i = 0; i <= m; ++i)
{
int op = (i % 2 == 0 ? 1 : -1);
f[i] = op * facinv[i] % mod;
g[i] = qpow(i, m, mod) * facinv[i] % mod;
}
f = f * g; f.resize(m+1);
ll ndownk = 1, ans = 0;
for(int k = 0; k <= m; ++k)
{
ll now = f[k] * ndownk % mod * fac[k] % mod * facinv[k+1] % mod * h(k,q) % mod;
ans = (ans + now) % mod;
ndownk = ndownk * (n-k) % mod;
}
ans = ans * qpow(1-p+mod, n, mod) % mod;
cout << ans << endl;
}
return 0;
}