zoukankan      html  css  js  c++  java
  • cf348D. Turtles(LGV定理 dp)

    题意

    题目链接

    (n imes m)有坏点的矩形中找出两条从起点到终点的不相交路径的方案数

    Sol

    Lindström–Gessel–Viennot lemma的裸题?

    这个定理是说点集(A = {a_1, a_2, dots a_n })(B = {b_1, b_2, dots b_n })的不相交路径条数等于

    [egin{bmatrix} e(a_1, b_1) & e(a_1, b_2) & dots & e(a_1, b_n) \ e(a_2, b_1) & e(a_2, b_2) & dots & e(a_2, b_n) \ dots & dots & dots & dots \ e(a_n, b_1) & e(a_n, b_2) & dots & e(a_n, b_n) \ end{bmatrix} ]

    的行列式的值。其中(e(x, y))表示从(x)(y)的路径条数

    定理的本质还是容斥

    回归到本题,我们需要找到两条不相交的路径。注意到任何一对合法的路径一定是一条从((1, 2))出发到((n - 1, m)),另一条从((2, 1))出发到((n, m - 1))

    那么选取(A = {(1, 2) (2, 1)}, B = {(n - 1, m) (n, m - 1)})

    带入到上述定理即可求解

    #include<bits/stdc++.h> 
    #define Pair pair<int, int>
    #define MP(x, y) make_pair(x, y)
    #define fi first
    #define se second
    #define int long long 
    #define LL long long 
    #define pt(x) printf("%d ", x);
    #define Fin(x) {freopen(#x".in","r",stdin);}
    #define Fout(x) {freopen(#x".out","w",stdout);}
    using namespace std;
    const int MAXN = 3001, INF = 1e9 + 10, mod = 1e9 + 7;
    const double eps = 1e-9;
    void chmax(int &a, int b) {a = (a > b ? a : b);}
    void chmin(int &a, int b) {a = (a < b ? a : b);}
    int sqr(int x) {return x * x;}
    int add(int x, int y) {if(x + y < 0) return x + y + mod; return x + y >= mod ? x + y - mod : x + y;}
    void add2(int &x, int y) {if(x + y < 0) x = x + y + mod; else x = (x + y >= mod ? x + y - mod : x + y);}
    int mul(int x, int y) {return 1ll * x * y % mod;}
    inline int read() {
        char c = getchar(); int x = 0, f = 1;
        while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int C[MAXN][MAXN], N, M;
    char A[MAXN][MAXN];
    int f(int a, int b, int c, int d) {
        memset(C, 0, sizeof(C));
        for(int i = a; i <= c; i++) 
            for(int j = b; j <= d; j++) 
                if(A[i][j] == '.') {
                    if(i == a && j == b) C[i][j] = 1;
                    else  C[i][j] = add(C[i - 1][j], C[i][j - 1]);              
                }
    
        return C[c][d];
    }
    void solve() {
        N = read(); M = read();
        for(int i = 1; i <= N; i++) scanf("%s", A[i] + 1);
        cout << add(mul(f(1, 2, N - 1, M), f(2, 1, N, M - 1)), -mul(f(1, 2, N, M - 1), f(2, 1, N - 1, M))) << '
    ';
    }
    signed main() {
        for(int T = 1; T; T--, solve());
        return 0;
    }
    
  • 相关阅读:
    Visual C#核心编程之泛型
    Visual C#核心编程之枚举器
    标准的非托管资源的销毁模式
    Visual C#核心编程之LINQ
    Visual C#核心编程之数组和集合
    ACCPSQL第四章上机六
    Visual C#2008核心编程之类型
    一月一代码 3月 kmp 领悟代码
    [转] 技巧 如何统一设置 windows live writer 的 图片大小
    understanding the linux virtual memory management 图序
  • 原文地址:https://www.cnblogs.com/zwfymqz/p/10195838.html
Copyright © 2011-2022 走看看