1. 程式人生 > >JZOJ 5164、【NOIP2017模擬6.25】小A做CF

JZOJ 5164、【NOIP2017模擬6.25】小A做CF

lds des dst ali 解法 參加 6.2 其它 ()

日常吐槽:我......已經不想再說什麽了,8:20還要做初賽然後很心累:)

題目:

Description

目標rating超過CLJ的小A最近在瘋狂做CF,話說這次CF在賽前十分鐘出了個相當奇葩的預選題,要求十分鐘內必須做出這道題才能參加這次CF。
這道題是這樣的,給你一個N*N的矩陣,每行有一個障礙,數據保證任意兩個障礙不在同一行,任意兩個障礙不在同一列,要求你在這個矩陣上放N枚棋子(障礙的位置不能放棋子),要求你放N個棋子也滿足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少種方案。
最近被各種神題折磨得死去活來,走火入魔的小A一下被這個超級水題給卡住了,但是這次是超過CLJ的最佳機會,於是,心急火燎的他找到了你,請你帶他順利參加這次CF正賽吧。

Input

第一行一個N,接下來一個N*N的矩陣。

Output

合法方案數。

Sample Input

2
0 1
1 0

Sample Output

1

Data Constraint

20%的數據保證: N<=10
60%的數據保證: N<=20
100%的數據保證: N<=200

題解:

20%爆搜+剪枝

60%爆搜+更好的剪枝來騙分(本驗題人未打過這檔分,所以拿不到的也沒辦法)

100%有兩種方法,一種是原題解方法,一種是本驗題人方法。

法一:錯排公式 (粘貼自百度百科)

遞推公式

當n個編號元素放在n個編號位置,元素編號與位置編號各不對應的方法數用D(n)表示,那麽D(n-1)就表示n-1個編號元素放在n-1個編號位置,各不對應的方法數,其它類推. 第一步,把第n個元素放在一個位置,比如位置k,一共有n-1種方法; 第二步,放編號為k的元素,這時有兩種情況:⑴把它放到位置n,那麽,對於剩下的n-1個元素,由於第k個元素放到了位置n,剩下n-2個元素就有D(n-2)種方法;⑵第k個元素不把它放到位置n,這時,對於這n-1個元素,有D(n-1)種方法; 綜上得到 D(n) = (n-1) [D(n-2) + D(n-1)] 特殊地,D(1) = 0, D(2) = 1.
下面通過這個遞推關系推導通項公式: 為方便起見,設D(k) = k! N(k), k = 1, 2, …, n, 則N(1) = 0, N(2) = 1/2. n ≥ 3時,n! N(n) = (n-1) (n-1)! N(n-1) + (n-1)! N(n-2) 即 nN(n) = (n-1) N(n-1) + N(n-2) 於是有N(n) - N(n-1) = - [N(n-1) - N(n-2)] / n = (-1/n) [-1/(n-1)] [-1/(n-2)]…(-1/3) [N(2) - N(1)] = (-1)^n / n!. 因此 N(n-1) - N(n-2) = (-1)^(n-1) / (n-1)!, N(2) - N(1) = (-1)^2 / 2!. 相加,可得 N(n) = (-1)^2/2! + … + (-1)^(n-1) / (n-1)! + (-1)^n/n! 因此 D(n) = n! [(-1)^2/2! + … + (-1)^(n-1)/(n-1)! + (-1)^n/n!]. 此即錯排公式。 (簡化:f[i]=(i-1)*(f[i-1]+f[i-2])

容斥原理

編輯 用容斥原理也可以推出錯排公式: 正整數1, 2, 3, ……, n的全排列有 n! 種,其中第k位是k的排列有 (n-1)! 種;當k分別取1, 2, 3, ……, n時,共有n*(n-1)!種排列是至少放對了一個的,由於所求的是錯排的種數,所以應當減去這些排列;但是此時把同時有兩個數不錯排的排列多排除了一次,應補上;在補上時,把同時有三個數不錯排的排列多補上了一次,應排除;……;繼續這一過程,得到錯排的排列種數為 D(n) = n! - n!/1! + n!/2! - n!/3! + … + (-1)^n * n!/n! = ∑(k=2~n) (-1)^k * n! / k!, 即D(n) = n! [1/0! - 1/1! + 1/2! - 1/3! + 1/4! + ... + (-1)^n/n!]. 其中,∑表示連加符號,k=2~n是連加的範圍;0! = 1,可以和1!相消。

二項式反演

利用二項式反演我們更為簡便的推導出一個通項公式。 考慮令 技術分享圖片 表示 技術分享圖片 個數字任意放的方案數, 技術分享圖片 表示 技術分享圖片 個數字都不放在自己位置上的方案數,通過枚舉不在自己位置上的數字的個數容易得到 技術分享圖片 由二項式反演得到 技術分享圖片 註意到 技術分享圖片 ,這樣我們就得到了他的通項公式,通過將 技術分享圖片 和組合數展開就可以得到更為簡便的通項公式了
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n, w[210][500], p[500], q[500], len = 1, l, k1;
void jia(int k)
{
    memset(q, 0, sizeof q);
    for (int i = 1; i <= len; i++)
        q[i] = w[k - 2][i] + w[k - 1][i];
    for (int i = 1; i <= len; i++)
    {
        q[i + 1] += q[i] / 10;
        q[i] %= 10;
    }
    while (q[len + 1]) len++;
}
void cheng(int k)
{
    jia(k);
    l = 0, k1 = k;
    memset(p, 0, sizeof p);
    while (k1 >= 10)
    {
        p[++l] = k1 % 10;
        k1 /= 10;
    }
    if (k1) p[++l] = k1;
    for (int i = 1; i <= len; i++)
        for (int j = 1; j <= l; j++)
            w[k][i + j - 1] += q[i] * p[j];
    for (int i = 1; i < len + l; i++)
    {
        w[k][i + 1] += w[k][i] / 10;
        w[k][i] %= 10;
    }
    len = len + l - 1;
    while (w[k][len + 1]) len++;
}
int main()
{
    memset(w, 0, sizeof w);
    scanf("%d", &n);
    w[1][1] = 1;
    w[2][1] = 2;
    for (int i = 3; i < n; i++) cheng(i);
    for (int i = len; i >= 1; i--) printf("%d", w[n - 1][i]);
    printf("\n");
    return 0;
}

法二:驗題人解法

基於轉化後的模型,可以發現,對於空白棋盤方案數是相當容易求出來的,而帶障礙棋盤方案可以通過容斥原理實現 (即將總的減去放一個棋子在障礙上再加回兩個棋子在障 礙上……)預處理出組合數(因為放障礙時要選位置),可以得出如下式子:(設有n行n列,m個障礙) Ans=∑(-1) i*C i m*(n-i)!(i 枚舉障礙數,由 0 到 m)

JZOJ 5164、【NOIP2017模擬6.25】小A做CF