习题:Iahub and Permutations(容斥)
题目
思路
这里的解法的说明应该是可以拓展的
首先简化问题,有\(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;
}

浙公网安备 33010602011771号