zoukankan      html  css  js  c++  java
  • 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题

    CodeForces - 348D Turtles

    这道题给你一个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
    ", ans);
        return 0;
    }
    View Code

    ====================================分割线===============================================

    ========================================================================================

    题目描述:

    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
    ", ans);
    89     }
    90 
    91     return 0;
    92 }
    View Code
  • 相关阅读:
    新浪微博OAuth2授权错误 error:redirect_uri_mismatch
    [Eclipse]自动注释功能
    android 官网访问不了
    [转]Java开发中的23种设计模式详解
    [转]JAVA Iterator 的用法
    [转] Java集合类详解
    MySQL(root用户)密码重置
    SpringMVC项目接入Springfox实战遇到的问题集合
    linux中mysql完整卸载命令操作
    linux-centos挂载新硬盘操作
  • 原文地址:https://www.cnblogs.com/chaoswr/p/9460378.html
Copyright © 2011-2022 走看看