这题在打比赛时,我感觉到了来自于世界的恶意。。。 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
目录
Lindström–Gessel–Viennot Lemma
AC代码:
题意:一个n x m 的矩阵,填入0,1,2三个数字,每行每列下标大的数字要填入大于等于前一个数字的值。
思路:将填入0,1和1,2看成两条不同的路,通过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 的逆序对数,为奇数则贡献为负,否则为正。
展开写,发现这就是一个行列式。
把对应的组合数换为路径数的记号的话,就是引理的内容了,证明同理。
后面补题的时候,也是极其难受。。。表示查了好多东西才看明白。。。
#include
#include
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;
}