CF340E Iahub and Permutations dp

题意
有一个长度为n的排列a,其中有一些位置被替换成了 1 。你需要尝试恢复这个排列,将 1 替换回数字。 求多少种可行方案使得得到的是一个排列且不存在 a i = i 的位置。答案 m o d   10 9 + 7

分析:
我们把数分为两类,第一类是 a i = 1 i 在其他位置没有出现过,第二类是 a i = 1 i 在其他位置出现过。对于第二种位置就相当于随便放,对于第一种位置就相当于错排。我们设 x 为第二类位置的个数, y 为第一类位置的个数。

我们考虑怎样构造错排序列,首先设 f [ i ] 为前 i 个数字的错排方案,当然不能放在 i 号位。则在前 i 1 个数中选择一个数 k ,有两种方案。第一种是 i k 调换,相当于对剩下 i 2 个数错排,即方案数为 f [ i 2 ] ;第二种是 k 放在不在 k 的任意位置,即 f [ i 1 ] 种方案。有

f [ i ] = ( i 1 ) ( f [ i 1 ] + f [ i 2 ] )

对于这一题,第 i 个位置不仅能放 i 还能放那 x 个数,因为这 x 个数可以随意放,也就是在 i 1 长度的错排后面加任意一个数,方案数为 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]);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81126584