瞬间移动(组合数, 逆元)

Posted handsomecui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了瞬间移动(组合数, 逆元)相关的知识,希望对你有一定的参考价值。

瞬间移动

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 263    Accepted Submission(s): 143

Problem Description
有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案,答案对1000000007取模。
![http://acm.hdu.edu.cn/data/images/C702-1003-1.jpg](http://acm.hdu.edu.cn/data/images/C702-1003-1.jpg)
 

 

Input
多组测试数据。
两个整数n,m(2n,m100000)
 

 

Output
一个整数表示答案
 

 

Sample Input
4 5
 

 

Sample Output
10
 

 

Source

题解:斜着看可以看出来其实是个杨辉三角,就是C(n + m - 4, m - 2);杨辉三角可以用逆元写;

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MOD = 1000000007;
typedef __int64 LL;
LL P[200010];
void init(){
    P[0] = 1;
    for(int i = 1; i <= 200000; i++){
        P[i] = P[i - 1] * i % MOD;
    }
}
LL quick_mul(LL a, LL n){
    LL ans = 1;
    while(n){
        if(n & 1){
            ans *= a;
            ans %= MOD;
        }
        n >>= 1;
        a *= a;
        a %= MOD;
    }
    return ans % MOD;
}
LL C(int n, int m){
    LL a = P[n];
    LL b = P[m] * P[n - m]%MOD;
    LL p = quick_mul(b, MOD - 2);
    return a * p % MOD;
}
int main(){
    init();
    int n, m;
    while(~scanf("%d%d", &n, &m)){
        printf("%I64d\n", C(n + m - 4, m - 2));
    }
    return 0;
}

 还有两种必定超时的方法:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1010;
int dp[MAXN][MAXN];
int main(){
    int n, m;
    while(~scanf("%d%d", &n, &m)){
        memset(dp, 0, sizeof(dp));
        for(int i = 2; i <= n; i++){
            for(int j = 2; j <= m; j++){
                for(int p = 1; p < i; p++){
                    for(int k = 1; k < j; k++){
                        dp[i][j] += dp[p][k];
                    }
                }
            }
        }
        printf("%d\n", dp[n][m]);
    }
    return 0;
}


记忆化搜索;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1010;
int dp[MAXN][MAXN];
int dfs(int x, int y){
    if(x <= 0 || y <= 0)
        return 0;
    if(dp[x][y])
        return dp[x][y];
    for(int i = 1; i < x; i++){
        for(int j = 1; j < y; j++){
            dp[x][y] += dfs(i, j);
        }
    }
    return dp[x][y];
}
int main(){
    int n, m;
    while(~scanf("%d%d", &n, &m)){
        dp[1][1] = 1;
        printf("%d\n", dfs(n, m));
    }
    return 0;
}

 

以上是关于瞬间移动(组合数, 逆元)的主要内容,如果未能解决你的问题,请参考以下文章

2016"百度之星" - 初赛(Astar Round2B)1003 瞬间移动 组合数学+逆元

HDU 5698 大组合数取模(逆元)

2016 百度之星 初赛B - 瞬间移动(逆元)

hdu5698瞬间移动(杨辉三角+快速幂+逆元)

组合数与除法逆元,阶乘逆元递推

组合数处理(逆元求解)...Orz