習題:Iahub and Permutations(容斥)
阿新 • • 發佈:2020-09-08
題目
思路
這裡的解法的說明應該是可以拓展的
首先簡化問題,有\(n\)個盒子,\(n\)個球,盒子有編號,球也有編號,只有\(cnt\)個編號盒子中有,並且球中也有這些編號
正難則反,總共的方案數就是\(n!\),要算的可以轉換為不合法的方案
即總共的方案可以轉換為恰好有一個盒子不滿足一直累加到恰好有\(cnt\)盒子不滿足
按照套路,將恰好有\(i\)個盒子不滿足轉換成為至少有\(i\)個盒子不滿足
那麼考慮如果有一個方案,它有\(t\)個盒子不滿足,那麼這個方案會被哪些條件統計到?
設\(f(i)\)為容斥係數,那麼一定滿足
\(1=\sum_{i=1}^{t}C_t^if(i)\)
那麼轉換為求\(f\)的值
如果\(f(t)\)已知,嘗試推到到\(f(t+1)\)
\(1=\sum_{i=1}^{t+1}C_{t+1}^if(i)=f(t+1)+\sum_{i=1}^{t}C_{t+1}^if(i)\)
那麼有\(f(t+1)=1-\sum_{i=1}^{t}C_{t+1}^if(i)\)
那麼接下來的工作就很簡單了,容斥係數已經求出,剩下的只剩統計至少\(i\)個的方案數了
很簡單,即為\(C_{cnt}^i(n-i)!\)
程式碼
#include<iostream> using namespace std; const int mod=1e9+7; int n; int a[2005],f[2005]; int tot,cnt; long long ans; long long fac[2005],inv[2005]; bool vis[2005],used[2005]; long long c(int n,int m) { return fac[n]*inv[m]%mod*inv[n-m]%mod; } long long qkpow(int a,int b) { if(b==0) return 1; if(b==1) return a; long long t=qkpow(a,b/2); t=t*t%mod; if(b&1) t=t*a%mod; return t; } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; fac[0]=1; inv[0]=1; for(int i=1;i<=n;i++) { fac[i]=i*fac[i-1]%mod; inv[i]=qkpow(fac[i],mod-2); } for(int i=1;i<=n;i++) if(a[i]==-1) { vis[i]=1; tot++; } for(int i=1;i<=n;i++) if(a[i]!=-1) used[a[i]]=1; for(int i=1;i<=n;i++) if(vis[i]==1&&used[i]==0) cnt++; f[1]=1; for(int i=2;i<=cnt;i++) { long long temp=0; for(int j=1;j<i;j++) temp=(temp+c(i,j)*f[j]%mod)%mod; f[i]=(1+mod-temp)%mod; } for(int i=1;i<=cnt;i++) { ans=(ans+f[i]*c(cnt,i)%mod*fac[tot-i]%mod)%mod; } cout<<((fac[tot]-ans)%mod+mod)%mod; return 0; }