[算法] 容斥
对于某些
毒瘤计数题,经常会出现统计重复或遗漏的问题,这时候就可能需要容斥一下
容斥原理
先从一个经典的例子入手:有三个学科,设为
根据题意,我们要求的就是:
考虑咋求,这是一个小学数学问题,直接用
其实这就是一个容斥;
扩展一下,将
这就是容斥原理;
简记为:奇加偶减;
对于其补集同理,有:
那么,对于集合的交,我们不难得到:
其中
对于右者使用容斥原理即可;
这就是比较常用的三个公式(其实都差不多);
应用
不定方程非负整数解计数
这是本篇文章主要研究的问题;
问题: 给出不定方程
没有限制
我们看作有
如果每个盒子至少有一个球的话,那么我们可以用插板法来做,对于可以为空的情况,我们可以再加
扩展一下,对于形如
其实上述问题就是
有限制
发现我们按照没有限制做会算多
考虑刚刚容斥原理中的第三个公式,答案可表示为:
其中对于
随便提出一项
其他项同理;
例题
可以看成
所以这题就是要对于每个
我们把原问题拆解成两个子问题:小于等于
对于小于等于
最后做个差分即可;
时间复杂度:依据调和级数是
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const long long mod = 1e9 + 7;
int n, m;
long long ksm(long long a, long long b) {
long long ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
long long fac[500005], fav[500005];
long long C(long long a, long long b) {
if (b < 0 || a < 0) return 0;
if (a == b) return 1;
if (a < b) return 0;
if (b == 0) return 1;
return fac[a] * fav[b] % mod * fav[a - b] % mod;
}
int a[500005];
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
fac[0] = 1;
fav[0] = 1;
for (int i = 1; i <= 500000; i++) {
fac[i] = fac[i - 1] * i % mod;
fav[i] = ksm(fac[i], mod - 2);
}
for (int k = 1; k <= m; k++) {
long long ans = 0;
long long su = 0;
for (int i = 0; i <= m / (k + 1); i++) {
ans = (ans + ((i & 1) ? -1ll : 1ll) * C(n - m + m - i * (k + 1), n - m) % mod * C(n - m + 1, i) % mod) % mod;
}
a[k] = ans;
}
for (int i = m; i >= 1; i--) {
a[i] = (a[i] - a[i - 1] % mod + mod) % mod;
}
for (int i = 1; i <= m; i++) {
cout << (a[i] + mod) % mod << ' ';
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通