题意
有一个长度为n的排列a,其中有一些位置被替换成了
。你需要尝试恢复这个排列,将
替换回数字。 求多少种可行方案使得得到的是一个排列且不存在
的位置。答案
。
分析:
我们把数分为两类,第一类是
且
在其他位置没有出现过,第二类是
且
在其他位置出现过。对于第二种位置就相当于随便放,对于第一种位置就相当于错排。我们设
为第二类位置的个数,
为第一类位置的个数。
我们考虑怎样构造错排序列,首先设
为前
个数字的错排方案,当然不能放在
号位。则在前
个数中选择一个数
,有两种方案。第一种是
与
调换,相当于对剩下
个数错排,即方案数为
;第二种是
放在不在
的任意位置,即
种方案。有
对于这一题,第 个位置不仅能放 还能放那 个数,因为这 个数可以随意放,也就是在 长度的错排后面加任意一个数,方案数为 ,把这两个加起来就是答案。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const LL mod=1e9+7;
const int maxn=2007;
using namespace std;
LL n,x,y;
LL a[maxn],f[maxn];
int main()
{
scanf("%lld",&n);
for (LL i=1;i<=n;i++) scanf("%lld",&a[i]);
for (LL i=1;i<=n;i++)
{
if (a[i]!=-1)
{
if (a[a[i]]==-1)
{
x++;
}
}
else y++;
}
y-=x;
f[0]=1;
for (LL i=2;i<=x;i++)
{
f[0]=(f[0]*i)%mod;
}
for (LL i=1;i<=y;i++)
{
f[i]=(f[i]+(i-1)*f[i-1]%mod)%mod;
if (i>=2) f[i]=(f[i]+(i-1)*f[i-2]%mod)%mod;
f[i]=(f[i]+x*f[i-1]%mod)%mod;
}
printf("%lld",f[y]);
}