牛客多校第一場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
目錄
題意:一個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;
}