2018.11.07 hdu1465不容易系列之一(二项式反演)

传送门

其实标签只是搞笑的。

没那么难。

二项式反演只是杀鸡用牛刀而已。

这道题也只是让你n≤20n\le20n≤20的错排数而已。

还记得那个O(n)O(n)O(n)的递推式吗?

没错那个方法比我今天用的要快一些。


言归正传。

回忆一下二项式反演的式子:

fn=∑i=0n(ni)gif_n=\sum_{i=0}^n\binom{n}{i}g_ifn​=∑i=0n​(in​)gi​

=>gn=∑i=0n((−1)i(nn−i)fi)g_n=\sum_{i=0}^n((-1)^i\binom{n}{n-i}f_i)gn​=∑i=0n​((−1)i(n−in​)fi​)

证明很简单。

只用把第一个式子成立的条件带到第二个等式的右边就可以了。

然后这道题怎么用呢?

我们令fif_ifi​表示iii张牌任意排列的总方案数。

gig_igi​表示iii张牌全部错排的方案数。

那么根据分类计数的原理显然有:

fn=∑i=0ngi=n!f_n=\sum_{i=0}^ng_i=n!fn​=∑i=0n​gi​=n!

于是gn=∑i=0n((−1)i(ni)fi)=∑i=0n((−1)in!(n−i)!)g_n=\sum_{i=0}^n((-1)^i\binom{n}{i}f_i)=\sum_{i=0}^n((-1)^i\frac{n!}{(n-i)!})gn​=∑i=0n​((−1)i(in​)fi​)=∑i=0n​((−1)i(n−i)!n!​)

做完了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=21;
ll fac[N];
int n;
int main(){
	fac[0]=1;
	for(int i=1;i<=20;++i)fac[i]=fac[i-1]*i;
	while(~scanf("%d",&n)){
		ll ans=0,tmp=1;
		for(int i=0;i<=n;++i,tmp*=-1)ans+=tmp*fac[n]/fac[i];
		cout<<ans<<'\n';
	}
	return 0;
}
上一篇:android多线程-AsyncTask之工作原理深入解析(上)


下一篇:camera理论基础和工作原理【转】