终于讲到反演定理了,反演定理这种东西记一下公式就好了,反正我是证明不出来的~(~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)
当然,我写的只是个形式,怎么可能这么简单。◕‿◕。
其实每一项再乘一个未知的函数就对了,但是这个函数我们不知道(不用担心,数学家已经帮我们解决了,我们直接用就可以了)
反演公式登场( >ω<)
c和d是两个跟n和r有关的函数
根据用法不同,c和d是不同的
一般数学家会先随便弄c函数
然后经过复杂的计算和证明,得到d函数
然后公式就可以套用了
正片开始
二项式反演公式
那个括号起来的就是组合数,我记得组合数那章我有说过
二项式反演也就是记住这个公式就算结束了
然后我们开始实战(/ω\)
容斥那章讲过的全错排(装错信封问题)
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代码:
1 #include<cstdio> 2 typedef long long LL; 3 int n, flag; 4 LL fac[25]; 5 LL ans; 6 void init(){ 7 fac[0] = 1; 8 for(int i = 1; i <= 20; i ++) fac[i] = fac[i-1] * i; 9 } 10 int main(){ 11 init(); 12 while(~scanf("%d", &n)){ 13 ans = 0; 14 flag = n & 1 ? -1 : 1;//起始符号 15 for(int i = 0; i <= n; i ++){ 16 ans += flag * fac[n] / fac[n-i]; 17 flag = -flag; 18 } 19 printf("%I64d\n", ans); 20 } 21 }