【2023杭电多校(5)】题解 #1008. Expectation (Hard Version)

题目链接: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 1m106,1n,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=0n(in)pi(1p)nij=0ijm
q = p 1 − p q=\dfrac{p}{1-p} q=1pp
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=(1p)ni=0n(in)qij=0ijm
因为 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=0m{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=(1p)ni=0n(in)qij=0ik=0m{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=(1p)nk=0m{km}i=0n(in)qij=0ijk
拆掉下降幂:
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=(1p)nk=0m{km}k!i=0n(in)qij=0i(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=(1p)nk=0m{km}k!i=0n(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)(mkrk),自然想到吸收恒等式:
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=(1p)nk=0m{km}k+1k!i=0n(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=(1p)nk=0m{km}k+1k!(kn)i=0n(iknk)(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=(1p)nk=0m{km}k+1nki=0n(iknk)(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=0n(iknk)(i+1)xi=i=0n(iknk)xi+1+C=xk+1j=0nk(jnk)xj+C=xk+1(1+x)nk+C=xk(1+x)nk1[(k+1)(1+x)+x(nk)]
这样就求出了 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=(1p)nk=0m{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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值