CF 451E Devu and Flowers

可重集的排列数 + 容斥原理

对于 \(\{A_1 * C_1, A _2 * C_2, \cdots, A_n * C_n\}\)这样的集合来说,
\(N = \sum_{i = 1} ^ n A_i\), 要在这个集合中取出 \(M\) 个元素来,这样的方案数是:

\[C _ {N+M-1}^{N-1} - \sum _ {i =1} ^ n {C_{N+M-A_i - 2}^{N-1}} + \sum _ {1\leq i < j \leq n} ^ n {C_{N+M - A_i - A_j -3}^{N-1}} \cdots +(-1)^N * C_{N+M-\sum_{i = 1}^n {c_i} - (N+1)}^{N-1} \]

我们可以通过枚举 \(1 \sim 2 ^ N - 1\)的数来表示这些组合数,对于一个数 \(x\) 来说,如果它的第 \(p\) 位上是 1 ,就表示减去 \(A_p\) ,若一共有 \(q\) 位是 1 ,则 一共减去 \(q\) 个元素,
在计算组合数 \(C_M^N\) 的时候,我们可以先计算 \(A_M^N\), 在乘上 \((N- 1)!\)的逆元.

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int MOD = 1000000007;
ll n, inv[100];
ll num[50], m, ans;
ll C(ll x, ll y) {
	if(x < 0 || y < 0 || x < y) return 0;
	x %= MOD;
	if(!y) return 1;
	if(!x) return 0;
	ll ans = 1ll;
	for(int i = 0; i < y; i++) {
		(ans *= (x - i) ) %= MOD;
	}
	(ans *= inv[y] ) %= MOD;
	return ans;
}
int main() {
	inv[0] = inv[1] = 1;
	for(int i = 2; i <= 30; i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
	for(int i = 2; i <= 30; i++) (inv[i] *= inv[i - 1]) %= MOD;
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> num[i];
	ans += C(m + n - 1, n - 1);
	for(int i = 1; i < (1 << n); i++) {
		ll t = m + n;
		int p = 0;
		for(int j = 0; j < n; j++) {
			if((i >> j) & 1) {
				p++;
				t -= num[j + 1];
			}
		}
		t -= p + 1;
		if(p & 1) {
			(ans -= C(t, n - 1)) %= MOD;
		}else (ans += C(t, n - 1)) %= MOD;
	}
	cout << (ans + MOD) % MOD << endl;
	return 0;
}
posted @ 2018-03-26 20:50  Mr_Wolfram  阅读(205)  评论(0编辑  收藏  举报