题意
有一个长度为n的排列a,其中有一些位置被替换成了−1−1。你需要尝试恢复这个排列,将−1−1替换回数字。 求多少种可行方案使得得到的是一个排列且不存在ai=iai=i的位置。答案mod 109+7mod 109+7。
分析:
我们把数分为两类,第一类是ai=−1ai=−1且ii在其他位置没有出现过,第二类是且ii在其他位置出现过。对于第二种位置就相当于随便放,对于第一种位置就相当于错排。我们设为第二类位置的个数,yy为第一类位置的个数。
我们考虑怎样构造错排序列,首先设为前ii个数字的错排方案,当然不能放在号位。则在前i−1i−1个数中选择一个数kk,有两种方案。第一种是与kk调换,相当于对剩下个数错排,即方案数为f[i−2]f[i−2];第二种是kk放在不在的任意位置,即f[i−1]f[i−1]种方案。有
f[i]=(i−1)∗(f[i−1]+f[i−2])f[i]=(i−1)∗(f[i−1]+f[i−2])
对于这一题,第ii个位置不仅能放还能放那xx个数,因为这个数可以随意放,也就是在i−1i−1长度的错排后面加任意一个数,方案数为f[i−1]∗xf[i−1]∗x,把这两个加起来就是答案。
代码:
#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]);
}