1. 程式人生 > >牛客多校第一場A——Monotonic Matrix(數論——組合數學—— Lindström–Gessel–Viennot lemma )

牛客多校第一場A——Monotonic Matrix(數論——組合數學—— Lindström–Gessel–Viennot lemma )

這題在打比賽時,我感覺到了來自於世界的惡意。。。 Lindström–Gessel–Viennot lemma定理,在比賽前,我是完全沒有聽說過的,好想哭,掛機的數論選手。。。

1. Count the number of n x m matrices A satisfying the following condition modulo (109+7). * Ai, j ∈ {0, 1, 2} for all 1 ≤ i ≤ n, 1 ≤ j ≤ m. * Ai, j ≤ Ai + 1, j for all 1 ≤ i < n, 1 ≤ j ≤ m. * Ai, j ≤ Ai, j + 1 for all 1 ≤ i ≤ n, 1 ≤ j < m.
輸入描述: The input consists of several test cases and is terminated by end-of-file. Each test case contains two integers n and m.

輸出描述: For each test case, print an integer which denotes the result. 備註 * 1 ≤ n, m ≤ 103 * The number of test cases does not exceed 10 5.
示例1:

輸入

1 2

2 2

1000 1000

輸出

6

20

540949876

目錄

AC程式碼:

題意:一個n x m 的矩陣,填入0,1,2三個數字,每行每列下標大的數字要填入大於等於前一個數字的值。

思路:將填入0,1和1,2看成兩條不同的路,通過Lindström–Gessel–Viennot Lemma來計算其中不同路徑的條數

Lindström–Gessel–Viennot Lemma

作為一個特殊情況,引理提供對給定一個起點終點對集(記起點集為 AA 終點集為 BB ,且路徑為 ai→biai→bi ),統計不相交路徑數。

引理的內容如下:

令 ω(P):=ω(P):= 路徑 PP 的邊權積, e(a,b):=∑P:a→bω(P)e(a,b):=∑P:a→bω(P) ,

M:=(e(a1,b1)e(a1,b2)⋯e(a1,bm) e(a2,b1)e(a2,b2)⋯e(a2,bm) ⋮⋮⋱⋮ e(an,b1)e(an,b2)⋯e(an,bm))M:=(e(a1,b1)e(a1,b2)⋯e(a1,bm) e(a2,b1)e(a2,b2)⋯e(a2,bm) ⋮⋮⋱⋮ e(an,b1)e(an,b2)⋯e(an,bm))


該引理宣稱,det|M|det|M| 給出AA 到 BB 不相交路徑的權值和。

特殊情況,令各邊邊權為11 ,此時|M||M| 為不相交路徑數。

更一般情況,ωω 可以是形式變數,則 ee 將成為形式冪級數。

假定我們在網格圖中,從第 11 行的起點走到第 nn 的終點。

首先假設我們只有兩對點 (a1,b1),(a2,b2)(a1,b1),(a2,b2) ,不考慮相交,則總路徑數為 (b1−a1+n−1n−1)(b2−a2+n−1n−1)(b1−a1+n−1n−1)(b2−a2+n−1n−1) .

考慮 a1→b1,a2→b2a1→b1,a2→b2 相交,則在最後一個相交點之後交換路徑,則得到 a1→b2,a2→b1a1→b2,a2→b1 的路徑。

考慮 a1→b2,a2→b1a1→b2,a2→b1 的路徑,其必相交,則在最後一個相交點之後交換路徑。

此時我們建立了 a1→b1,a2→b2a1→b1,a2→b2 的相交路徑與 a1→b2,a2→b1a1→b2,a2→b1 的一一對應。

所以除去不合法情況後,答案為 (b1−a1+n−1n−1)(b2−a2+n−1n−1)−(b1−a2+n−1n−1)(b2−a1+n−1n−1)(b1−a1+n−1n−1)(b2−a2+n−1n−1)−(b1−a2+n−1n−1)(b2−a1+n−1n−1) .

接著考慮多組路徑,使用容斥原理。

假設某些路徑相交,則在最後一個相交點之後交換路徑,得到 BB 的重排 CC .

所以我們只需考慮 CC 的逆序對數,為奇數則貢獻為負,否則為正。

展開寫,發現這就是一個行列式。

把對應的組合數換為路徑數的記號的話,就是引理的內容了,證明同理。

後面補題的時候,也是極其難受。。。表示查了好多東西才看明白。。。

AC程式碼:

#include<bits/stdc++.h>
#include<ctime>
using namespace std;
const int N = 2e3 + 7;
const int mod = 1e9 + 7;
long long jie[N],ni[N];
void get_jie()
{
    jie[0] = 1;
    for(int i = 1; i < N;++i)   jie[i] = (jie[i - 1] * i) % mod;
}
long long fpow_mod(long long a,long long b)
{
    long long res = 1;
    while(b){
        if(b & 1) res = (res * a) % mod;;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res;
}
void get_inv()
{
    for(int i = 0;i < N;++i)
        ni[i] = fpow_mod(jie[i],mod - 2);
}
inline long long con(int n,int m)
{
    return (jie[n] * ni[m]) % mod * ni[n - m] % mod;
}
int main()
{
    int n,m;
    get_jie();
    get_inv();
    while(cin >> n >> m){
        long long ans = 0;
        ans = con(n + m,n);
        ans = (ans * ans) % mod;
        ans = ans - (con(n + m,m - 1) * con(n + m,n - 1)) % mod;
        /*if(n == 1 || m == 1){
            if(n < m) swap(n,m);
            ans = 2 + n + (n + 2) * (n - 1) / 2;
        }*/
        ans = (ans + mod) % mod;
        cout << ans << endl;
    }
    return 0;
}