容斥

1.多重集的组合数

这里问题一般的描述为:

 

算法竞赛进阶指南上有详细的推导。

例题1: CF 451E Devu and Flowers

直接带公式,但本题 m 太大,先用Lucas对m取模,再转换成排列再乘以逆元就可以了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[30];
const ll mod = 1e9 + 7;
ll inv[30];
///C(y,x)
ll quick_pow(ll a, ll n)
{
    ll res = 1;
    while(n)
    {
        if(n & 1LL) res = res * a % mod;
        n >>= 1LL;
        a = a * a % mod;
    }
    return res;
}
ll C(ll y, ll x)
{
 //   printf("%lld %lld\n", y,x);
    if(y < 0 || x < 0 || y < x) return 0;
    y %= mod;
    if(y == 0 || x == 0) return 1;
    ll res = 1;
    for(int i = 0; i < x; i++)
    {
        res = res * (y - i) % mod;
    }
    for(int i = 1; i <= x; i++)
    {
        res = res * inv[i] % mod;
    }
   // printf("%lld\n", res);
    return res;
}
int main()
{
    ll n, m;
    cin >> n >> m;
    for(int i = 0; i <= 20; i++)
    {
        inv[i] = quick_pow(i, mod - 2);
       // printf("%lld\n", inv[i]);
    }
    for(int i = 1; i <= n; i++) cin >> f[i];
    ll ans = 0;
    for(int x = 0; x < (1 << n); x++)
    {
        if(x == 0)
        {
            ans = (ans + C(n + m - 1, n - 1)) % mod;
        }
        else
        {
            int p = 0;
            ll t = n + m;
            for(int i = 0; i < n; i++)
            {
                if((1 << i) & x)
                {
                    t -= f[i + 1];
                    p++;
                }
            }
            t -= (p + 1);
            if(p & 1)
            {
                ans = (ans - C(t, n - 1) + mod) % mod;
            }
            else
            {
                ans = (ans + C(t, n - 1)) % mod;
            }

        }
      //  cout << ans << endl;
    }
    cout << ans << endl;
    return 0;
}
Code

猜你喜欢

转载自www.cnblogs.com/littlepear/p/9380814.html