LGV定理 (CodeForces 348 D Turtles)/(牛客暑期多校第一场A Monotonic Matrix)
又是一个看起来神奇无比的东东,证明是不可能证明的,这辈子不可能看懂的,知道怎么用就行了,具体看wikihttps://en.wikipedia.org/wiki/Lindstr%C3%B6m%E2%80%93Gessel%E2%80%93Viennot_lemma
LGV定理就是求n个起点到n个终点(且一个起点对应一个终点)的不相交路径数目的一种方法,光看这句话不好理解,来看一道cf题
这道题给你一个n*m的矩阵(1~n, 1~m),现在有两只乌龟都从左上角(1,1)出发,到达右下角(n,m),乌龟每次只能往右或者往下走一格,且两只乌龟走的路径不能交叉,问一共有多少种走法。首先显然可知的是一只乌龟必然从(1,2)走到(n-1, m),另一只必然从(2,1)走到(n,m-1),这个画画图就知道很显然了,那么我们可以看作有两个2个起点,2个终点,问共多少种不相交路径,这就是LGV定理了。
LGV定理实际上是求一个行列式
ai是起点,bi是终点,e(ai, bj)代表从ai为起点到bj为终点的方法数目,特别的注意ai对应bi(就是我们想求的起点到终点的方案数)
那么对于这道题,就相当于求一个2x2行列式的值。
|e1, e2|
|e3, e4|
其中
e1 = a1(2,1)-->b1(n,m-1) 的方案数
e2 = a1(2,1)-->b2(n-1,m) 的方案数
这两个用一遍dp求出来
e3 = a2(1,2)-->b1n,m-1) 的方案数
e4 = a2(1,2)-->b2(n-1,m) 的方案数
这两个也用一遍dp求出来
然后只要代入行列式,就能求得a1-b1,a2-b2且路径不相交的方法数ans=(e1*e4-e2*e3)了
#include <iostream> #include <string.h> #include <cstdio> #include <vector> #include <queue> #include <stack> #include <math.h> #include <string> #include <algorithm> #include <functional> #define SIGMA_SIZE 26 #define lson rt<<1 #define rson rt<<1|1 #define lowbit(x) (x&-x) #define foe(i, a, b) for(int i=a; i<=b; i++) #define fo(i, a, b) for(int i=a; i<b; i++) #define pii pair<int,int> #pragma warning ( disable : 4996 ) using namespace std; typedef long long LL; inline LL LMax(LL a, LL b) { return a>b ? a : b; } inline LL LMin(LL a, LL b) { return a>b ? b : a; } inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm inline int Max(int a, int b) { return a>b ? a : b; } inline int Min(int a, int b) { return a>b ? b : a; } inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = 1e9+7; const double eps = 1e-8; const int inf = 0x3f3f3f3f; const int maxk = 3e6 + 5; const int maxn = 7e7 + 5; bool g[3010][3010]; int row, col; char str[3010]; LL dp[3010][3010]; void init() { cin >> row >> col; for (int i = 1; i <= row; i++ ) { scanf("%s", str+1); for ( int j = 1; j <= col; j++ ) { if (str[j]=='#') g[i][j] = true; } } } pair<LL, LL> getRoad(int x, int y) { memset(dp, 0, sizeof(dp)); dp[1][1] = 1; for (int i = x; i <= row; i++) { for (int j = y; j <= col; j++) { if (!g[i][j]) dp[i][j] = (dp[i][j]+dp[i-1][j]+dp[i][j-1])%mod; } } return make_pair(dp[row][col-1], dp[row-1][col]); } int main() { init(); LL x1, x2, x3, x4; pair<LL, LL> tmp; tmp = getRoad(2, 1); x1 = tmp.first; x2 = tmp.second; tmp = getRoad(1, 2); x3 = tmp.first; x4 = tmp.second; LL ans = ((x1*x4)%mod-(x2*x3)%mod+mod)%mod; printf("%lld\n", ans); return 0; }
====================================分割线===============================================
========================================================================================
题目描述:
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.
这道题就没这么裸了,我们考虑01分界线和12分界线,都可以看作是从(n,0)出发,到(0,m)的两条路,而且根据题意这两条路不能相交,是不是和LGV很像了呢?然而LGV定理中路线重合也算相交,在这道题中路线是可以重合的,比如如果没有1只有0和2,那么01和12分界线是完全重合的,然后这道题很神奇的操作就是把01分界线向左向上移动了一格,这样两条路必然不会重合了(画画图),而且也变成了两个起点两个终点,
a1 = (n-1, -1) a2 = (n, 0)
b1 = (-1, m-1) b2 = (0, m)
经过这个神奇的操作后就直接套用LGV就行了.....因为这题不像上一道CF路径中间有障碍,所以求方法数只要简单的用排列组合+阶乘逆元就行了(比如(n,0)--->(0,m),一共要走n+m步,其中选n步向上,选m步向右,就是算个组合数了)
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <vector> 5 #include <queue> 6 #include <stack> 7 #include <math.h> 8 #include <string> 9 #include <algorithm> 10 #include <functional> 11 12 #define SIGMA_SIZE 26 13 #define lson rt<<1 14 #define rson rt<<1|1 15 #define lowbit(x) (x&-x) 16 #define foe(i, a, b) for(int i=a; i<=b; i++) 17 #define fo(i, a, b) for(int i=a; i<b; i++) 18 #define pii pair<int,int> 19 #pragma warning ( disable : 4996 ) 20 21 using namespace std; 22 typedef long long LL; 23 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 24 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 25 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 26 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 27 inline int Max(int a, int b) { return a>b ? a : b; } 28 inline int Min(int a, int b) { return a>b ? b : a; } 29 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 30 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 31 const LL INF = 0x3f3f3f3f3f3f3f3f; 32 const LL mod = 1e9+7; 33 const double eps = 1e-8; 34 const int inf = 0x3f3f3f3f; 35 const int maxk = 3e6 + 5; 36 const int maxn = 2010; 37 38 LL fac[maxn]; 39 LL inv[maxn]; 40 41 LL quickPow(LL a, LL b, LL m) 42 { 43 LL ans = 1, base = a; 44 while (b) { 45 if (b & 1) 46 ans = (ans*base) % m; 47 base = (base*base) % mod; 48 b >>= 1; 49 } 50 return ans; 51 } 52 53 void init() 54 { 55 fac[0] = fac[1] = 1; 56 inv[0] = inv[1] = 1; 57 58 for( int i = 2; i < maxn; i++ ) 59 fac[i] = (fac[i-1]*i) % mod; 60 61 inv[maxn-1] = quickPow(fac[maxn-1], mod-2,mod); 62 for( int i = maxn-2; i >= 2; i-- ) 63 inv[i] = (inv[i+1]*(i+1)) % mod; 64 } 65 66 LL C(int up, int down) 67 { 68 if (up > down) return 0; 69 return fac[down]*inv[down-up]%mod*inv[up]%mod; 70 } 71 72 73 //a1 = (n-1, -1) a2 = (n, 0) 74 //b1 = (-1, m-1) b2 = (0, m) 75 int main() 76 { 77 init(); 78 int row, col; 79 while (~scanf("%d %d", &row, &col)) 80 { 81 LL x1, x2, x3, x4; 82 x1 = C(row, row+col); 83 x2 = C(col+1, row+col); 84 x3 = C(col-1, row+col); 85 x4 = x1; 86 87 LL ans = ((x1*x4)%mod - (x2*x3)%mod + mod) % mod; 88 printf("%lld\n", ans); 89 } 90 91 return 0; 92 }