博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数论17——反演定理(二项式反演)
阅读量:6765 次
发布时间:2019-06-26

本文共 3281 字,大约阅读时间需要 10 分钟。

终于讲到反演定理了,反演定理这种东西记一下公式就好了,反正我是证明不出来的~(~o ̄▽ ̄)~o

 

 

 

 

 

 

 

首先,著名的反演公式

我先简单的写一下o( ̄ヘ ̄*o)

比如下面这个公式

f(n) = g(1) + g(2) + g(3) + ... + g(n)

如果你知道g(x),蓝后你就可以知道f(n)了

 

如果我知道f(x),我想求g(n)怎么办

这个时候,就有反演定理了

反演定理可以轻松的把上面的公式变为

g(n) = f(1) + f(2) + f(3) + ... + f(n)

 

当然,我写的只是个形式,怎么可能这么简单。◕‿◕。

 

其实每一项再乘一个未知的函数就对了,但是这个函数我们不知道(不用担心,数学家已经帮我们解决了,我们直接用就可以了)

 

 

 

 

 

 

反演公式登场( >ω<)

 

反演定理1

 

c和d是两个跟n和r有关的函数

根据用法不同,c和d是不同的

一般数学家会先随便弄c函数

然后经过复杂的计算和证明,得到d函数

然后公式就可以套用了

 

 

 

 

 

 

 

 

 

 

 

正片开始

 

二项式反演公式

 

二项式反演1

 

那个括号起来的就是组合数,我记得组合数那章我有说过

组合数4

 

 

二项式反演也就是记住这个公式就算结束了

 

然后我们开始实战(/ω\)

 

 

 

 

 

 

 

 

 

容斥那章讲过的全错排(装错信封问题)

hdu 1465

http://acm.hdu.edu.cn/showproblem.php?pid=1465

 

 

 

 设g(i)表示正好有i封信装错信封

那么全部的C(n, i)*g(i)加起来正好就是所有装信的情况,总共n!种情况

n! = Σ C(n, i)*g(i) (i从0到n)

那么f(n) = n!,所以f(x) = x!

那么我们要求g(n)

根据公式

 

g(n) = Σ (-1)^(n-i) * C(n, i) * f(i)  (i从0到n)

 

那么就可以计算啦~\(≧▽≦)/~

 

AC代码:

#include
typedef long long LL;int n, flag;LL fac[25];LL ans;void init(){ fac[0] = 1; for(int i = 1; i <= 20; i ++) fac[i] = fac[i-1] * i; }int main(){ init(); while(~scanf("%d", &n)){ ans = 0; flag = n & 1 ? -1 : 1;//起始符号 for(int i = 0; i <= n; i ++){ ans += flag * fac[n] / fac[n-i]; flag = -flag; } printf("%I64d\n", ans); }}
View Code

 

是不是很好用但是不容易想到T_T

这也没有办法

 

再来一题吧

还是容斥那一章讲过的题目的

UVALive 7040

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5052

 

题意:给n盆花涂色,从m种颜色中选取k种颜色涂,保证正好用上k种颜色,你必须用上这k种颜色去涂满n个相邻的花,并且要求相邻花的颜色不同,求方案数。

 (1 ≤ n, m ≤ 1e9 , 1 ≤ k ≤ 1e6 , k ≤ n, m)

 

 

 

 

首先,用k种颜色涂花,假如不考虑全部用上,那么总的方案数是多少

第一盆花有k种颜色选择,之后的花因为不能跟前一盆花的颜色相同,所以有k-1种选择

于是总方案数为k*(k-1)^(n-1)

我们设必须用 i 种颜色两两不相邻的涂花的方案数为 g(i)

那么

k*(k-1)^(n-1) = Σ C(k, i)*g(i) (i从1到k)

令f(k) = k*(k-1)^(n-1),那么f(x) = x*(x-1)^(n-1)

二项式反演公式出现了

所以g(k) = Σ (-1)^(k-i) * C(k, i) * f(i)  (i从1到k)

 

 

最终的答案就是C(m, k) * g(k)

(这里m有1e9,C(m, k)直接用for循环算,直接for循环从m*(m-1)*...*(m-k+1)再乘k的阶乘的逆元)

 

 

 

 

AC代码:

#include
typedef long long LL;const int N = 1000000 + 5;const int MOD = (int)1e9 + 7;int F[N], Finv[N], inv[N];LL pow_mod(LL a, LL b, LL p){ LL ret = 1; while(b){ if(b & 1) ret = (ret * a) % p; a = (a * a) % p; b >>= 1; } return ret;}void init(){ inv[1] = 1; for(int i = 2; i < N; i ++){ inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD; } F[0] = Finv[0] = 1; for(int i = 1; i < N; i ++){ F[i] = F[i-1] * 1ll * i % MOD; Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD; }}int comb(int n, int m){ if(m < 0 || m > n) return 0; return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;}int main(){ init(); int T, n, m, k, ans, flag, temp; scanf("%d", &T); for(int cas = 1; cas <= T; cas ++){ scanf("%d%d%d", &n, &m, &k); ans = 0; flag = (k & 1) ? 1 : -1; //计算g(k) for(int i = 1; i <= k; i ++){ ans = (ans + 1ll * flag * comb(k, i) * i % MOD * pow_mod(i-1, n-1, MOD) % MOD) % MOD; flag = -flag; } //接下来计算C(m, k) temp = Finv[k]; for(int i = 1; i <= k; i ++){ temp = 1ll * temp * (m-k+i) % MOD; } ans = ((1ll * ans * temp) % MOD + MOD) % MOD; printf("Case #%d: %d\n", cas, ans); }}
View Code

 

 

做完两题之后就感觉二项式反演变得容易了,遇到题目还是要多想( ̄▽ ̄")

等等。。。做完两题的我突然发现二项式反演和容斥推倒出来的公式总是一样的。。。。。。(为什么有种被骗的感觉T_T)

转载于:https://www.cnblogs.com/xzxl/p/7354141.html

你可能感兴趣的文章
ElementUI的Table组件中的renderHeader方法研究
查看>>
PTPD2源码解析之:packet的接收和发送
查看>>
javascript appendChild()的完整功能
查看>>
Java多线程基础(一)——线程与锁
查看>>
Python每日一练0012
查看>>
Vue.js入门教程-methods
查看>>
研究人员用 AI 评估小血管病变,可预测病人患中风和痴呆的概率
查看>>
C++ 采集音频流(PCM裸流)实现录音功能
查看>>
Oracle Instant Client(即时客户端) 安装与配置
查看>>
windows环境下 生成git公钥和私钥
查看>>
ONVIF测试方法及工具
查看>>
JQuery实战---窗口效果
查看>>
最好用的Android黑客应用程序和工具
查看>>
揭秘白帽黑客:优秀女白帽子比大熊猫还稀罕
查看>>
MySQL配置文件my.ini参数注释说明
查看>>
矩阵的乘法算法
查看>>
跨服务器查询
查看>>
Memory Barriers/Fences
查看>>
Android之SpannableString、SpannableStringBuilder总结
查看>>
自定义注解
查看>>