zoukankan      html  css  js  c++  java
  • hdu 5698 组合数

    瞬间移动

    Problem Description

    有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第nn行第mm列的格子有几种方案,答案对10000000071000000007取模。

    http://acm.hdu.edu.cn/data/images/C702-1003-1.jpg

    Input

    多组测试数据。

    两个整数n,m(2leq n,mleq 100000)n,m(2n,m100000)

    Output

    一个整数表示答案

    Sample Input
    4 5
    Sample Output
    10

    Source

    分析:

    画几个数之后就可以发现规律了,可以看出是一个倾斜45度的杨辉三角,每个位置的的数相当于:从杨辉三角第(n+m-4)层,取m-2个数。

    因为会有除法取模,所以要用逆元。

    有两个公式:

    这两个公式实际上是等价的,只是用代码实现的时候会有所差别。

    我是用第二个写的:

    #include<cstdio>
    #include<cstring>
    const long long mod=1000000007;
    typedef long long ll;
    const int N=100002;
    ll inv[N];
    int main()
    {
        int n,m;
        inv[1]=1;
        for(int i=2;i<N;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        while(~scanf("%d%d",&n,&m)){
            if(n==1||m==1){
                printf("0
    ");continue;
            }
            n=n+m-4;
            m=m-2;
            ll ans=1;
            for(int i=1;i<=m;i++){
                ans=(ans*(ll)(n-i+1))%mod;
                ans=(ans*inv[i])%mod;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    当然逆元也有很多种实现方法:

    可以用扩展欧几里德定理,费马小定理(这需要快速幂实现),或者上面那个递推公式。

    对于这题,是用杨辉三角来做的,也有人的思路是:

    给我们一个坐标,我们可以得出可以移动到的区域即(n-2)*(m-2)。枚举要在这个区域停留几次,C(y,k),表示从y列中选k列去停,C(x,k)表示从x行中选哪k行去停。所以乘积累加即为结果。

    这种《问题分解》的思想是值得学习的,就像UVa 11134这题的思想是一致的。


  • 相关阅读:
    npm安装elasticsearch-reindex
    Linux14_文本编辑器与终端配置
    Linux13_群组的管理和文件权限的管理
    Linux12_用户和权限
    Linux11_文件及目录以及其相关的组织命令
    Linux10_常用命令和操作
    Linux9_安装Linux系统
    基础概念——什么是POSIX
    C++Review21_宏和常量
    C++Review20_C++函数高级特性(重载、内联、const和virtual)
  • 原文地址:https://www.cnblogs.com/01world/p/5651203.html
Copyright © 2011-2022 走看看