CF 341C: Iahub and Permutations

題目鏈接:

http://codeforces.com/contest/341/problem/C


題目大意:

給定一個含有N個位置的序列,某些位置上的數字已經確定,某些位置上的數字沒有確定。

求這個序列可能產生多少種錯排(a[i] != i)。


算法:

這題的做法基本就是DP或容斥。


先說一下DP的解法。感謝wuyiqi大牛的耐心講解。  >_<

顯然所有已經填好的位置可以不管,我們只看 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 ! 。


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

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

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

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


容斥的做法,就是枚舉至少有多少個 i 使得a[i] = i,具體可以看ftiasch的代碼


PS:

這題對我來說還算是稍有意義。

因爲這場比賽我的後一個半小時都在想這個。也沒別的事好幹。

這種感覺簡直太可怕了。QAQ  若菜一把辛酸淚啊


代碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <climits>
#include <cmath>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;

const int MOD = 1000000007;
const int MAXN = 2100;
int a[MAXN];
bool hash[MAXN];
long long d[MAXN];

int main()
{
    int n;
    scanf("%d", &n);
    memset(hash, 0, sizeof(hash));
    for(int i = 0; i < n; i ++)
    {
        scanf("%d", &a[i]);
        if(a[i] != -1)
        {
            hash[a[i]] = true;
        }
    }
    int x = 0, y = 0;
    for(int i = 0; i < n; i ++)
    {
        if(a[i] == -1)
        {
            if(hash[i + 1])
            {
                x ++;
            }
            else
            {
                y ++;
            }
        }
    }
    d[0] = 1;
    for(int i = 1; i <= x; i ++)
    {
        d[0] = d[0] * i % MOD;
    }
    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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章