二项式反演
二项式反演
(写这么个玩意也不容易,如果发现有错请及时联系我qwq)
1. 反演的定义
演绎推理是我们在数学中经常遇到的方法。对于数列来说,通过原数列计算出新数列叫作演绎,而通过计算出的数列反推出原数列则被称为反演。
举个例子,假设有两个数列 和 , 为原数列, 为新数列。我们从 推出 的过程就叫做演绎,而利用 反推出 的过程就叫做反演。
一般来说,有了两个数列的相互关系,我们才能相互推算。在一些特定的情况下,相互关系会有一些特殊的性质,人们将重要的、常见的相互关系命名,并深入研究其性质,如二项式反演、莫比乌斯反演、单位根反演、子集反演等。
再举个例子,我们假设 与 满足:
我们可以利用它们的关系式,从 推到 ,也可以通过解 元一次方程组,从 推到 。当然,这个关系式的反演过程只需要简单地进行解方程组,所以并没有特别的名字。
2. 通过容斥原理推导二项式反演
不会容斥原理的可以来看我的另一篇博客
我们设全集 中的任意 个元素的并集大小相等,任意 个元素的交集大小也相等。
设 代表任意 个集合的交集, 代表任意 个集合的补集的交集。特别地,
那么我们就能得到下面两条容斥式子:
所以我们得到了优美的 一点都不优美的 二项式反演式子:
3. 二项式反演的常用形式
形式一
已知公式:
我们假设 ,则有:
即
而对于整数 ,,所以在对 取模的意义下,有 。
所以有
即常见的:
这也就是第一个常用的形式,不过这里 与 的定义视情况而定。
形式二
先给出形式:
将右式代入左式,则
等式两边恒等,故恒成立。
4. 二项式反演在题目中的应用
4.1 几个经典问题
4.1.1 全错位排列问题
错位就是原来在某一位置上的数不能在这个位置上,或者说
全排列就不再解释了
对于这个问题,我们可以定义 表示 个数中,至多有 个 的总方案数。 表示 个数的全错位排列的方案数。
所以有:
从 的定义来看,有
所以我们可以用二项式反演得到 :
在求和式中,把 提出来,再用 代换 ,式子不变,得到
这也就是所谓的全错位排列公式。
4.1.2 第一类斯特林数
想了解斯特林数的可以看看我的另一篇博客,这里只是大概描述一下斯特林数所解决的基本问题。
有 个不同的球放入 个不同的盒子,要求每个盒子非空,球有多少种方案数。
这里的限制是“非空”,那我们就从这里入手,定义 表示 个不同的球放入 个不同的盒子,至多有 个盒子非空的方案数, 表示 个不同的球放入 个不同的盒子,恰好有 个盒子非空的方案数。
易得:
二项式反演后得:
4.1.3 第二类斯特林数
一样,也在我那篇博客里有详细介绍,这里只是粗略说下所解决的基本问题。
有 个不同的球放入 个相同的盒子,要求每个盒子非空,球有多少种方案数。
其实它与第一类斯特林数就不同在了盒子是否相同,所以我们的定义与推导过程基本不变,只不过最终的式子再除以 即可
4.1.3 计数问题
重点就在于怎么找到限制条件 性质,有了限制条件 性质,就能找到 与 的含义,也就能通过二项式反演轻松解决掉问题。
4.1.3.1 题目大意
给定 ,并给定 以及 ,要求两两配对使得 的对数减去 的对数等于 。( 中无相同元素)
4.1.3.2 分析
比较明显的计数问题。既然明确告诉我们要求恰好有 个,那我们就可以很容易想到我们二项式反演。
4.1.3.3 题解
设有 对 满足 ,那么有 对 满足 ,也就意味着我们要让 即 。
现在我们要考虑怎么求恰好有 个 ,自然过渡到用二项式反演解决。
设 表示至少有 对 的方案数, 表示恰好有 对 的方案数。
那么有:
那我们现在就要去考虑如何计算 。
我们先将 和 分别从小到大排序。设 表示比 小的 的个数, 表示前 个 中(排序后)恰好有 对 的方案数。可以列出状态转移方程:
而 就等于
有了这些, 就很好求了。
4.1.3.4 代码
点击查看代码
#include<bits/stdc++.h>
#define M 2007
#define mod 1000000009
#define int long long //我习惯不好,别学我
using namespace std;
int n, k, x, ans;
int a[M], b[M], F[M][M], r[M], fac[M], g[M], inv[M];
inline int quick_pow(int base, int p) {
int res = 1;
while(p) {
if(p & 1)
res = res * base % mod;
base = base * base % mod;
p >>= 1;
}
return res;
}
inline int C(int a, int b) {return fac[a] * inv[b] % mod * inv[a - b] % mod;}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= n; ++ i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = quick_pow(fac[i], mod - 2);
}
x = (n + k) >> 1;
for(int i = 1; i <= n; ++ i)
cin >> a[i];
for(int i = 1; i <= n; ++ i)
cin >> b[i];
stable_sort(a + 1, a + 1 + n);
stable_sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; ++ i)
r[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b - 1;
F[0][0] = 1;
for(int i = 1; i <= n; ++ i) {
F[i][0] = 1;
for(int j = 1; j <= i; ++ j)
F[i][j] = (F[i - 1][j] + (r[i] - j + 1) * F[i - 1][j - 1] % mod) % mod;
}
for(int i = x; i <= n; ++ i)
g[i] = F[n][i] * fac[n - i] % mod;
for(int i = x; i <= n; ++ i) {
if((i - x) & 1)
ans = (ans - C(i, x) * g[i] % mod + mod) % mod;
else
ans = (ans + C(i, x) * g[i] % mod) % mod;
}
cout << ans;
}
4.2 方法总结
总的来说,把握好至少 至多与恰好的关系是最重要的。
4.2.1 至少与恰好
假设共有 种不同的性质, 表示从有若干个元素的集合中,选出若干个至少有 种不同性质的元素的集合总方案数, 表示从有若干个元素的集合中,选出若干个恰好有 种不同性质的元素的集合方案数。
那么根据我们刚才的结论,有:
4.2.1 至多与恰好
假设共有 种不同的性质, 表示从有若干个元素的集合中,选出若干个至多有 种不同性质的元素的集合总方案数, 表示从有若干个元素的集合中,选出若干个恰好有 种不同性质的元素的集合方案数。
那么根据我们刚才的结论,有:
5. 多元二项式反演 高维二项式反演
(个人感觉挺难的)
5.1 结论
设有 个非负整数 。假设 及 都是整数,且:
那么有:
注:
5.2 证明
既然你已经看到这里了,说明你很强了,所以我接下来就不写那么细了
我们先来证明一个小东西 恶心人的东西,我们一会需要用到:
证明如下:
当 时,原式等于
当 时,原式利用二项式定理可以继续化简:
得证。
我们再继续证我们最初要证的东西:
利用刚刚证明的引理,我们可以发现只有当 时, 的值才不为 ,为 。
所以原式等于 ,得证。
5.3 例题
一道简单的 恶心的 二元 二维二项式反演
式子给你,我就不写题解了(你自己写着玩去吧,我懒)
6. 总结 后记
二项式反演在很多问题中用处还是不小的,尤其是计数问题,很多都用到了二项式反演,有能力的话还是搞得明白一点比较好。
(呃呃呃,终于写完了,整个人都不好了)
(写这么个玩意也不容易,如果发现有错请及时联系我qwq)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!