zoukankan      html  css  js  c++  java
  • ccf559c

    题意:给出一个矩阵棋盘,大小不超过10^5。上面有n个非法点(n<=2000)不可以踩,从左上角开始走到右下角,每次只能向下或者向右移动。问有多少种走法。结果对10^9+7取模。

    分析:

    组合数学DP

    设dp[i]表示从左上角开始不经过非法点走到第i个非法点有多少种方法。

    dp[i]=num(s, point[i]) - sigma( dp[j] * num(point[j], point[i]) );

    其中,sigma表示加和,s是起始点,j遍历所有在点i左上方的点。num(a,b)表示从a到b有多少种走法(不用避开非法点)。

    num可以直接用组合数表示,即c(x+y-2, x -1)。

    这道题涉及到了较大的组合数取模问题。求大组合数方法如下:

    可以先处理出所有1~n的阶乘,存入数组。factorial[i]表示i的阶乘取模后的结果。

    并求出所有的阶乘在该模运算下的逆元,存入数组。inverse[i]表示factorial[i]的逆元。

    c(n,m)原本等于( n! / (n-m)! ) / m!,现在用这两个数组可以表示为factorial[n] * inverse[n-m] * inverse[m]。

    模板如下:

    #define LL long long
    
    const int MAX_M = (int)(2e5) + 20;
    const int MOD = (int)(1e9) + 7;
    
    LL multi_mod(LL a, LL b, LL c)
    {    //返回(a*b) mod c,a,b,c<2^63
        a %= c;
        b %= c;
        LL ret = 0;
        while (b)
        {
            if (b & 1)
                ret = (ret + a) % c;
            a <<= 1;
            a %= c;
            b >>= 1;
        }
        return ret;
    }
    
    LL pow_mod(LL x, LL n, LL mod)
    {  //返回x^n mod c ,非递归版
        x %= mod;
        if (n == 1)
            return x;
        LL ret = 1;
        while (n)
        {
            if (n & 1)
                ret = multi_mod(ret, x, mod);
            n >>= 1;
            x = multi_mod(x, x, mod);
        }
        return ret;
    }
    
    long long get_inverse(long long a)
    {
        return pow_mod(a, MOD - 2, MOD);
    }
    
    long long factorial[MAX_M];
    long long inverse[MAX_M];
    
    void init_comb(int n)
    {
        factorial[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            factorial[i] = factorial[i - 1] * i;
            factorial[i] %= MOD;
        }
        inverse[n] = get_inverse(factorial[n]);
        for (int i = n - 1; i >= 0; i--)
        {
            inverse[i] = inverse[i + 1] * (i + 1);
            inverse[i] %= MOD;
        }
    }
    
    long long comb(long long a, long long b)
    {
        long long ret = factorial[a] * inverse[a - b];
        ret %= MOD;
        ret *= inverse[b];
        ret %= MOD;
        return ret;
    }
    View Code

    求逆元可以使用扩展欧几里德算法,但是比较麻烦。这次我使用了一个更为简单的算法,费马小定理。

    即:当p是质数且a和p互质,那么a^(p-1)=1 (mod p)

    而逆元的定义是x * y=1 (mod p)则y是x的逆元。令x=a,且a与p互质,则由a^(p-1)=1 (mod p)可得:y=a^(p-2)。

    对于求a的逆元这个问题,a<p且p是质数,自然可以利用上面的结论,a的逆元就是a^(p-2)。

    求逆元模板如下:

    LL multi_mod(LL a, LL b, LL c)
    {    //返回(a*b) mod c,a,b,c<2^63
        a %= c;
        b %= c;
        LL ret = 0;
        while (b)
        {
            if (b & 1)
                ret = (ret + a) % c;
            a <<= 1;
            a %= c;
            b >>= 1;
        }
        return ret;
    }
    
    LL pow_mod(LL x, LL n, LL mod)
    {  //返回x^n mod c ,非递归版
        x %= mod;
        if (n == 1)
            return x;
        LL ret = 1;
        while (n)
        {
            if (n & 1)
                ret = multi_mod(ret, x, mod);
            n >>= 1;
            x = multi_mod(x, x, mod);
        }
        return ret;
    }
    
    long long get_inverse(long long a)
    {
        return pow_mod(a, MOD - 2, MOD);
    }
    View Code

    本题代码如下:

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    #define d(x) 
    #define LL long long
    
    const int MAX_N = 2020;
    const int MAX_M = (int)(2e5) + 20;
    const int MOD = (int)(1e9) + 7;
    
    int row_num, col_num;
    int n;
    pair<int, int> point[MAX_N];
    long long dp[MAX_N];
    
    void input()
    {
        scanf("%d%d%d", &row_num, &col_num, &n);
        for (int i = 0; i < n; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            point[i] = make_pair(a, b);
        }
        point[n++] = make_pair(row_num, col_num);
        point[n++] = make_pair(1, 1);
        for (int i = 0; i < n; i++)
        {
            point[i].first--;
            point[i].second--;
        }
    }
    
    LL multi_mod(LL a, LL b, LL c)
    {    //返回(a*b) mod c,a,b,c<2^63
        a %= c;
        b %= c;
        LL ret = 0;
        while (b)
        {
            if (b & 1)
                ret = (ret + a) % c;
            a <<= 1;
            a %= c;
            b >>= 1;
        }
        return ret;
    }
    
    LL pow_mod(LL x, LL n, LL mod)
    {  //返回x^n mod c ,非递归版
        x %= mod;
        if (n == 1)
            return x;
        LL ret = 1;
        while (n)
        {
            if (n & 1)
                ret = multi_mod(ret, x, mod);
            n >>= 1;
            x = multi_mod(x, x, mod);
        }
        return ret;
    }
    
    long long get_inverse(long long a)
    {
        return pow_mod(a, MOD - 2, MOD);
    }
    
    long long factorial[MAX_M];
    long long inverse[MAX_M];
    
    void init_comb(int n)
    {
        factorial[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            factorial[i] = factorial[i - 1] * i;
            factorial[i] %= MOD;
        }
        inverse[n] = get_inverse(factorial[n]);
        for (int i = n - 1; i >= 0; i--)
        {
            inverse[i] = inverse[i + 1] * (i + 1);
            inverse[i] %= MOD;
        }
    }
    
    long long comb(long long a, long long b)
    {
        long long ret = factorial[a] * inverse[a - b];
        ret %= MOD;
        ret *= inverse[b];
        ret %= MOD;
        return ret;
    }
    
    int main()
    {
        init_comb(200000);
        input();
        sort(point, point + n);
        dp[0] = 1;
        for (int i = 1; i < n; i++)
        {
            dp[i] = comb(point[i].first + point[i].second, point[i].second);
            for (int j = 1; j < i; j++)
            {
                if (point[j].first <= point[i].first && point[j].second <= point[i].second)
                {
                    long long a = point[i].first - point[j].first;
                    a += point[i].second - point[j].second;
    
                    long long b = point[i].second - point[j].second;
    
                    dp[i] -= (dp[j] * comb(a, b)) % MOD;
                    dp[i] += MOD;
                    dp[i] %= MOD;
                }
            }
            d(printf("dp[%d] = %d
    ", i, (int)dp[i]));
        }
        printf("%d
    ", (int)dp[n - 1]);
        return 0;
    }
    View Code
  • 相关阅读:
    决定Java程序员工资高低的三个因素
    Java常见面试题汇总(七)
    Java的 RMI与RPC的区别
    2019新版Java学习路径
    JAVA中String,StringBuilder以及StringBuffer三个类之间的区别
    2019Java开发学习路线图
    bzoj1458 士兵占领
    bzoj1303 [CQOI2009]中位数图
    bzoj3721 [PA2014 Final] Bazarek
    bzoj2659 [Beijing wc2012]算不出的算式
  • 原文地址:https://www.cnblogs.com/rainydays/p/4706219.html
Copyright © 2011-2022 走看看