codeforces 341C Iahub and Permutations(組合數dp)

果7發表於2013-10-07
C. Iahub and Permutations
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Iahub is so happy about inventing bubble sort graphs that he's staying all day long at the office and writing permutations. Iahubina is angry that she is no more important for Iahub. When Iahub goes away, Iahubina comes to his office and sabotage his research work.

The girl finds an important permutation for the research. The permutation contains n distinct integers a1a2, ..., an (1 ≤ ai ≤ n). She replaces some of permutation elements with -1 value as a revenge.

When Iahub finds out his important permutation is broken, he tries to recover it. The only thing he remembers about the permutation is it didn't have any fixed point. A fixed point for a permutation is an element ak which has value equal to k (ak = k). Your job is to proof to Iahub that trying to recover it is not a good idea. Output the number of permutations which could be originally Iahub's important permutation, modulo 1000000007 (109 + 7).

Input

The first line contains integer n (2 ≤ n ≤ 2000). On the second line, there are n integers, representing Iahub's important permutation after Iahubina replaces some values with -1.

It's guaranteed that there are no fixed points in the given permutation. Also, the given sequence contains at least two numbers -1 and each positive number occurs in the sequence at most once. It's guaranteed that there is at least one suitable permutation.

Output

Output a single integer, the number of ways Iahub could recover his permutation, modulo 1000000007 (109 + 7).

Sample test(s)
input
5
-1 -1 4 3 -1
output
2
Note

For the first test example there are two permutations with no fixed points are [2, 5, 4, 3, 1] and [5, 1, 4, 3, 2]. Any other permutation would have at least one fixed point.



題目大意:給你1~n個數字,要求錯排,不過-1說明這個數字還不確定,但是給定了非-1數字,說明這個數字已經定了下來。開始很莽撞的直接用錯排數計算。。。實際上不然,填數字的時候,有一些是可以亂填沒有限制的,因為這些數字已經被開始填了,有一些則有限制,比如給兩組資料。

5
-1 -1 4 3 -1
限 限      限
5
-1 -1 2 5 -1
限 非      非
看第二組資料,錯排是a[i]!=i,而2和5都被佔用了,那麼2,5都成了非限制的了,然而1是限制的。

解題思路:開始看了傳說中的frog蛙神的部落格,表示看了一晚上都不解,若菜都是一把淚水啊!邊看邊問,終於理解了其中的要義,感謝frog1902的講解。



下面是蛙神寫的思路:

顯然所有已經填好的位置可以不管,我們只看 a[i] = -1 的位置。

x 表示目前有多少個位置使得a[i] = -1 且數字 i 已經被填在某個位置上,稱為無限制位置。

y 表示目前有多少個位置使得a[i] = -1 且數字 i 沒有被填在某個位置上,稱為有限制位置。


那麼,最一開始的時候,我們先把 y 個有限制位置拋棄不看。

因為a[i] = -1 且 i 也沒有出現在任何位置上,所以忽略不看不會有影響。


現在我們先把 x 個無限制的位置填好。

由於有 x 個數的 a[i] = -1 但是數字 i 已經被使用了。所以也一定有 x 個數的a[i] != -1但是數字 i 還沒被用。

就把這 x 個沒使用的數字放在 x 個被填的位置上。方法數是 x! 。


現在我們把 y 個無限制的位置一個接一個的加進來

用 d[i] 表示已經有多少個無限制的位置被加進來,且不違反錯排的規則。

顯然d[0] = x ! 。我們所要求的就是d[y].


前面都比較好理解,主要就是下面這一部分,比較難理解了。

為了更好的理解,我拿個例子輔助說明。

如果此時我們把第 i 個有限制的位置加進來。分以下幾種情況:

1)我們從 x 個無限制的的位置中找一個 j ,令a[i] = a[j],a[j] = i。規約到d[i - 1]的方案數。

5
-1 -1 2 5 -1
限 非      非
從2,5中選一個,使得a[1]=2,a[2]=1,就變成了d[i-1].

2)我們從i - 1個有限制的位置中找一個位置 j ,令a[i] = j,a[j] = i。規約到d[i - 2]的方案數。

5
-1 -1 3 4 -1
限 限      限
假如此時a[2]=5,a[5]=2,那麼就變成了d[i-2].

3)我們從i - 1個有限制的位置中找一個位置 j ,令a[i] = j,但是a[j] != i。規約到d[i - 1]的方案數。也就相當於由原來的 d[j] != j 限制變為了d[j] != i,其它限制不變。

5
-1 -1 3 4 -1
限 限      限
假如此時a[2]=5,a[5]=1,那麼就變成了d[i-1].

AC程式碼:

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
int mod=1e9+7;
__int64 d[2002];
int a[2002];

__int64 cal(int p)
{
    __int64 ans=1;
    int i;
    for(i=2;i<=p;i++)
        ans=(ans*i)%mod;
    return ans;
}

int main()
{
    int n,x,y;
    while(~scanf("%d",&n))
    {
        int cnt=0,t=0;  //cnt記錄可以填的個數,t記錄填的數字被佔用的個數
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]==-1)
                cnt++;
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]>0)
            {
                if(a[a[i]]==-1)
                    t++;
            }
        }

        x=t;  //x是無限制的個數
        y=cnt-t;
        d[0]=cal(x);   //d[0]=x!
        for(int i=1;i<=y;i++)
        {
            d[i]=((x+i-1)*d[i-1])%mod;
            if(i>1)
                d[i]=(d[i]+(i-1)*d[i-2])%mod;
        }
        printf("%I64d\n",d[y]);
    }
    return 0;
}

/*
5
-1 -1 4 3 -1
5
-1 -1 4 -1 -1
5
-1 -1 2 5 -1
*/



相關文章