bzoj3622 ( 容斥,二项式反演)

https://vjudge.d0j1a1701.cc/problem/黑暗爆炸-3622
image
直接考虑每个人至少一个物品不好想,反过来考虑恰好0人没有物品。
f(i)表示钦定i个人没有物品,f(i)= \(\left(\begin{array}{c} n \\ i \end{array}\right) \prod_{j=1}^{m}\left(\begin{array}{c} n-i+a_{j}-1 \\ a_{j} \end{array}\right) 。\)
即从n个人中选i个不发物品,其他n-i个"随意"。 随意的意思是其他人有没有物品都行,是m个相同小球方n个不同盒子可以空盒的模型
二项式反演得到恰好0人没有物品
image

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
//#define int long long
const int N = 2e3 + 5;
const int M = 1e3 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps = 1e-13;
const int MATN = 102;
ll F[N];ll inv[N];
ll qmi(ll m, ll k) {
    ll res = 1;
    while(k) {
        if(k & 1) res = res * m % mod;
        m = m * m % mod;
        k >>= 1;
    }
    return res;
}
void init(int n){
    F[0]=inv[0]= 1;
    for(int i=1;i<=n;i++)F[i]=F[i-1]*i%mod;//求阶乘
    inv[n]=qmi(F[n],mod - 2);//随便求一下n的逆元
    for(int i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;//由最后一个往前推
}
ll C(ll n, ll m) {
    return F[n] * inv[m] % mod * inv[n - m] % mod;
}
ll num[1005];
int main() {
    IOS
    init(2000);
    int n, m; cin >> n >> m;
    for ( int i = 1; i <= m; ++ i ) {
        cin >> num[i];
    }
    ll ans = 0; ll t = -1;
    for ( int j = 0; j <= n; ++ j ) {
        if(j & 1) t = -1; else t = 1;
        ll tmp = t * C(n, j) % mod;
        for ( int i = 1; i <= m; ++ i ) {
            tmp = tmp * C(num[i] + n - j - 1, num[i]) % mod;
        }
        ans = (ans + tmp) % mod;
    }
    cout << (ans + mod) % mod << '\n';
    return 0;
}




posted @ 2022-10-17 17:40  qingyanng  阅读(44)  评论(0编辑  收藏  举报