Codeforces Round #198 (Div. 2) E. Iahub and Permutations

题目描述:戳这里

题解:

这题是一道组合+递推题。
首先直接错排是不行的,因为有一些位置上原来与它配对的点已经被其它某一个点占用了,所以要重新思考。
既然有这种占用的情况,我们注意到有几个点被占用了,那么原本与占用这个点的位置配对的点就空下来了。我们可以将它们先补到那些被占用点的位置上去。我们假设这些点的个数为sumx,那么剩下的没有任何限制条件又未被占用的点的个数,我们设为sumy。那么就可以递推一下了。
设f[i]表示sumy个点里面推到第i个点的方案数,那么根据错排公式,f[i]=(i - 1)*(f[i - 1] + f[i-2])。
但是考虑到还可以放那些空闲的点,我们考虑将这个位置的点和前面sumx中补空的点交换的方案,即f[i]=f[i - 1]*sumx。那么就要注意f[0]=x!(将sumx个数补位的方案数)

代码如下:

#include
#include
using namespace std;
const int maxn=2005,tt=1e9+7;
int n,m,sumx,sumy,a[maxn],b[maxn],hsh[maxn];
long long f[maxn];
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {scanf("%d",&a[i]); if (a[i]!=-1) hsh[a[i]]=1,m++;}
    for (int i=1;i<=n;i++)
    if (a[i]==-1&&hsh[i]) sumx++;
    sumy=n-m-sumx;
    f[0]=1ll;
    for (int i=1;i<=sumx;i++) f[0]=(f[0]*i)%tt;
    for (int i=1;i<=sumy;i++){
        if (i>1) f[i]=(f[i]+(i-1)*f[i-2]%tt)%tt;
        f[i]=(f[i]+(i-1)*f[i-1]%tt)%tt;
        f[i]=(f[i]+f[i-1]*sumx%tt)%tt;
    }
    printf("%lld\n",f[sumy]);
    return 0;
}

你可能感兴趣的:(题解,CodeForces题解)