1. 程式人生 > >2018.11.07 hdu1465不容易系列之一(二項式反演)

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

傳送門 其實標籤只是搞笑的。 沒那麼難。 二項式反演只是殺雞用牛刀而已。 這道題也只是讓你n20n\le20的錯排數而已。 還記得那個O(n)O(n)的遞推式嗎? 沒錯那個方法比我今天用的要快一些。

言歸正傳。 回憶一下二項式反演的式子: fn=i=0n(ni)gif_n=\sum_{i=0}^n\binom{n}{i}g_i =>gn=i=0n((1)i(nni)fi)g_n=\sum_{i=0}^n((-1)^i\binom{n}{n-i}f_i) 證明很簡單。 只用把第一個式子成立的條件帶到第二個等式的右邊就可以了。 然後這道題怎麼用呢? 我們令f

if_i表示ii張牌任意排列的總方案數。 gig_i表示ii張牌全部錯排的方案數。 那麼根據分類計數的原理顯然有: fn=i=0ngi=n!f_n=\sum_{i=0}^ng_i=n! 於是gn=i=0n((1)i(ni)fi)=i=0n((1)in!(ni)!)g_n=\sum_{i=0}^n((-1)^i\binom{n}{i}f_i)=\sum_{i=0}^n((-1)^i\frac{n!}{(n-i)!}) 做完了。 程式碼:

#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; }