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