Loading

Devu和鲜花(容斥原理)

题意:Devu 有 N 个盒子,第 i 个盒子中有 Ai 枝花。同一个盒子内的花颜色相同,不同盒子内的花颜色不同。Devu 要从这些盒子中选出 M 枝花组成一束,求共有多少种方案。若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。结果需对 109+7 取模之后方可输出。

  • 先不考虑带限制条件的情况(每个盒子中的花数量为无限),即从N个盒子中每个选择0枝花,最后组成M枝花,很明显的一个球盒问题,且允许为空答案即为(n+m1n1)
  • 考虑带上限制,我们反着思考其补集,答案即为不带限制的答案减去至少有一个盒子取出的花的数量不满足限制
  • S1,S2,,Sn分别代表第1,2,,n个盒子中取出的花的枝数不符合题目要求,此时要先拿出A[i]+1枝花先分配出去,剩下的部分就可看做没有限制的球盒模型了,方案数即为(n+m1(A[i]+1)n1)。由此可知答案即为所求(n+m1n1)|S1S2Sn|
  • 后面的集合并式很明显可以用容斥原理拆开解决,从[0,2n1]枚举集合的状态第i位为1代表选择Si的集合求交,为0则代表不选,符合由选择的集合个数决定

主要就是面对这种求方案正求不好求的时候,可以考虑容斥原理,变换一下问题的形式,而操作容斥原理,即为枚举集合的2n种状态。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int mod = 1e9 + 7;

int ksm(int a,int b) {
	int res = 1;
	while(b) {
		if(b & 1) res = 1ll * res * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1;
	}
	return res;
}

int down;

int C(ll n,ll m) {
	if(n < m) return 0;
	int up = 1;
	for(ll i = n;i > n - m;i --) {
		up = 1ll * up * i % mod;
	}
	// cout << up << ' ' << down << '\n';
	return 1ll * up * down % mod;
}

int main() {
	ll n,m; cin >> n >> m;
	vector<ll>A(n + 1);
	for(int i = 1;i <= n;i ++) {
		cin >> A[i];
	}
	down = 1;
	for(int i = 1;i < n;i ++) {
		down = 1ll * down * i % mod;
	}
	down = ksm(down,mod - 2);
	int ans = 0;
	for(int S = 0;S < 1 << n;S ++) {//容斥原理 枚举2^n这种情况
		ll a = n + m - 1,b = n - 1;
		int count = 0;
		for(int j = 0;j < n;j ++) {
			if(S >> j & 1) {
				count ++;
				a -= A[j + 1] + 1;
			}
		}
		int sign = count & 1 ? -1 : 1;
		// cout << a << ' ' << b << ' ' << sign << '\n';
		ans = (ans + C(a,b) * sign) % mod;
	}

	ans = (ans + mod) % mod;
	cout << ans << '\n';
	return 0;
}
posted @   x7x7g7c7  阅读(563)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2021-05-31 NWERC2020 A - Atomic Energy (背包 + 思维)
点击右上角即可分享
微信分享提示