牛客小白赛 23 D.病毒传染(条件概率,枚举,容斥,二项式反演)

*在这里插入图片描述


注意题目是条件概率,设 a n s [ i ] ans[i] ans[i] 表示一开始有 i i i 个人携带病毒,party 结束后有 k k k 个人得病的概
率,对于每一个 i i i,答案是 a n s [ i ] ∑ i = 1 k a n s [ i ] \displaystyle\frac{ans[i]}{\sum_{i = 1}^kans[i]} i=1kans[i]ans[i]

考虑如何求 a n s [ i ] ans[i] ans[i] a n s [ i ] = C n i ∗ p i ∗ ( 1 − p ) n − i ∗ C n − i k − i ∗ f ( k − i ) C e m ans[i] = C_n^i*p^i*(1-p)^{n-i}*C_{n-i}^{k-i}*\frac{f(k-i)}{C_e^m} ans[i]=Cnipi(1p)niCnikiCemf(ki)

其中 e e e = n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n(n1)

f ( k − i ) f(k-i) f(ki) 表示指定的 k − i k - i ki 个人被传染的接触方案数。

直接求 f ( x ) f(x) f(x) 不好求,定义 g ( x ) g(x) g(x) 表示指定的 x x x 个人中,最多 x x x 个人被传染的方案数。

容易得出 g ( x ) = ( e − t ∗ ( n − t − x ) m ) g(x) = \binom{e - t*(n-t-x)}{m} g(x)=(met(ntx)) g ( x ) = ∑ i = 0 x C x i f ( x ) g(x) = \displaystyle\sum_{i = 0}^xC_x^if(x) g(x)=i=0xCxif(x)

通过二项式反演,可以得到 f ( x ) = ∑ i = 0 x ( − 1 ) x − i ∗ C x i ∗ g ( i ) f(x) = \displaystyle\sum_{i = 0}^x(-1)^{x-i}*C_x^i*g(i) f(x)=i=0x(1)xiCxig(i)

带入得到 a n s [ i ] = C n i ∗ p i ∗ ( 1 − p ) n − i ∗ C n − i k − i ∗ ( C e m ) − 1 ∗ ∑ j = 0 k − i ( − 1 ) k − i − j ∗ C k − i j ∗ ( e − i ∗ ( n − i − j ) m ) ans[i] = C_n^i*p^i*(1-p)^{n-i}*C_{n-i}^{k-i}*(C_e^m)^{-1}*\displaystyle\sum_{j = 0}^{k-i}(-1)^{k-i-j}*C_{k-i}^j*\binom{e -i*(n-i-j)}{m} ans[i]=Cnipi(1p)niCniki(Cem)1j=0ki(1)kijCkij(mei(nij))

(二项式反演定义的是对指定的 x 个,恰好 x 个满足条件和至多 x 个满足条件的转换)


代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
ll fpow(ll a,ll b) {
    
    
    ll r = 1;
    while (b) {
    
    
        if (b & 1) r = 1ll * r * a % mod;
        b >>= 1;
        a = 1ll * a * a % mod;
    }
    return r;
}
ll n,m,k,p,fac[maxn],ifac[maxn],ans[maxn],sum;
ll C(int x,int y) {
    
    
    if (x < y || y < 0 || x < 0) return 0;
    return 1ll * fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
int main() {
    
    
    fac[0] = 1;
    for (int i = 1; i <= maxn - 10; i++)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    ifac[maxn - 10] = fpow(fac[maxn - 10],mod - 2);
    for (int i = maxn - 10 - 1; i >= 0; i--)
        ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
    scanf("%lld%lld%lld%lld",&n,&m,&k,&p);
    ll q = 100 - p;
    ll t1 = 1ll * p * fpow(100,mod - 2) % mod, t2 = 1ll * q * fpow(100,mod - 2) % mod;
    ll siz = n * (n - 1) / 2;
    ll tot = C(n * (n - 1) / 2,m);                  //m对接触的所有情况
    for (int i = 1; i <= k; i++) {
    
                       //枚举 i 个人带病毒
        ans[i] = 1ll * C(n,i) * fpow(t1,i) % mod * fpow(t2,n - i) % mod * fpow(tot,mod - 2) % mod * C(n - i,k - i) % mod;
        ll cnt = 0; ll s = 1;
        for (int j = k - i; j >= 0; j--) {
    
    
        	cnt = (cnt + s * C(k - i,j) % mod * C(siz - i * (n - i - j),m) % mod) % mod;
        	if (cnt < 0) cnt += mod;
        	s *= -1;
		}
        ans[i] = 1ll * ans[i] * cnt % mod;
        sum = (sum + ans[i]) % mod;
    }
    for (int i = 1; i <= k; i++) {
    
    
        printf("%lld\n",ans[i] * fpow(sum,mod - 2) % mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/105024427