• HDU 5698 瞬间移动


    题面:

    传送门

    瞬间移动

    Input file: standard input
    Output file: standard output
    Time limit: 2 second
    Memory limit: 256 megabytes
     
    有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案,答案对1000000007取模。
     
     
     
     
    Input
    多组测试数据。

    两个整数n,m(2n,m100000)
     
    Output
    一个整数表示答案
     
    Example
    Input
    4 5
    Output
    10
     

    题目描述:

     

    题目分析:

    这题用到的知识点:快速幂,组合数,逆元与费马小定理。我们先分析一下样例:从格子(1, 1)到格子(4, 5):
    从这里我们可以看到,其中一种方案就是:直接到达终点。其次,我们想到的方案是通过中间一个格子来到达终点:

    最后,我们可以选取中间两个格子来到达终点:
    一共10种情况,这里我们是以中间选多少格子来分类讨论的:
    中间不选格子:1种
    中间选1个格子:6种
    中间选2个格子:3种
    那么,我们怎样计算出中间选x个格子有多少种情况?
    我们先看中间选1个格子是怎样选出来的:
    当我们确定了1行和1列的时候,就可以确定1个格子的位置。按照组合数学的知识,我们可以知道中间选一个格子的方案数为:C(2, 1)*C(3, 1),也就是6。
     
    当我们中间选2个格子的时候:
    这时,我们确定了2行2列,因为每一次我们只能选右下方的格子,所以我们选择右下方对角线方向的直线的交点,这时2个格子的位置就被确定了。
    方案数:C(2, 2)*C(3, 2),也就是3。
    所以,到达格子(4, 5)的方案数就是:C(4-2, 0)*C(5-2, 0) + C(4-2, 1)*C(5-2, 1) + C(4-2, 2)*C(5-2, 2)。
    如果是到达格子(5, 4),其实方案数也是一样的(对称关系),只要看成(4, 5)的情况就好了。
     
    有的人可能有疑问:中间如果要选3个格子或以上怎么办?其实道理和上面一样,我就不多说直接上图吧:
     
    所以计算公式是:C(n-2, 0)*(m-2, 0) + C(n-2, 1)*(m-2, 1) + ...... + C(n-2, n-2)*C(m-2, n-2)  (n <= m 时)
    如果 n > m,直接交换 n,m 就行了。
     
    这时,我们计算组合数C(a, b) = a! / ( b! * (a-b)! )。因为组合数太大,所以答案要求我们对组合数进行取模,也就是C(a, b) % p (p == 1000000007)。但是取模就涉及到一个问题:我们可以对含有乘法的运算进行边乘边取模,但是不能对含有除法的运算进行取模运算。举个例子:对于乘法来说,(6*3)%4 == (6%4)*3%4;但是对于除法来说,(6/3)%4 != (6%4) / (3%4) %4  (左侧结果是2,右侧结果是0)。为了把 / ( b! * (a-b)! ) 等效为一个乘法的运算,也就是除于一个数 x 取模的结果,等于乘于一个数 y 取模的结果,我们就要用到逆元的知识。逆元的作用就是:如果数 w,x,y,p符合这样的运算:w / x % p == w * y % p,那么y就是x的逆元。如果p是质数,那么根据费马小定理(这里就不推导了( ̄▽ ̄)"),这个 y == x p-2 。而这个 x p-2 怎么算?如果直接求速度太慢了,这时就要用到快速幂,把O(p-2)的时间复杂度降到O( log(p-2) )。最后,我们预处理一下数据就行了。记得要边乘边模才不会溢出(做完一次乘法就立刻取模,注:取模运算符的优先级和乘除法是一样的)
     
     
    AC代码:
     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 const long long mod = 1000000007;
     7 long long f[100005];
     8 long long inv[100005];
     9 
    10 long long quick_pow(long long base, long long x){  //快速幂
    11     long long ans = 1;
    12     while(x){
    13         if(x & 1) ans = ans*base%mod;
    14         base = base*base%mod;
    15         x >>= 1;
    16     }
    17     return ans%mod;
    18 }
    19 
    20 long long comb(int a, int b){           //组合数
    21     return f[b]*inv[a]%mod*inv[b-a]%mod;
    22 }
    23 
    24 void fac(){     //预处理
    25     f[0] = 1;
    26     inv[0] = 1;
    27     for(int i = 1; i <= 100005; i++){
    28         f[i] = f[i-1]*i%mod;          //得出阶乘
    29     }
    30     for(int i = 100005; i > 0; i--){
    31         inv[i] = quick_pow(f[i], mod-2);   //得出阶乘对应的逆元
    32     }
    33 }
    34 
    35 int main(){
    36     int n, m;
    37     fac();
    38     while(~scanf("%d%d", &n,&m)){
    39         if(n > m) swap(n, m);     //交换n, m, 使小的在前,大的在后
    40         n = n-2;
    41         m = m-2;
    42         long long ans = 0;
    43         for(int i = 0; i <= n; i++){
    44             ans = (ans + comb(i, n)%mod*comb(i, m)%mod)%mod;   //推导出的公式
    45         }
    46         cout << ans << endl;
    47     }
    48     return 0;
    49 }
     
     
     
     
     
  • 相关阅读:
    JavaScript高级程序设计:第十二章
    JavaScript高级程序设计:第九章
    PageHelper的使用方法
    dbutils的环境搭建以及使用
    Spring的xml中引入其他文件
    Spring AOP
    SpringMVC拦截器
    中文乱码的过滤器
    SpringMVC的处理器全局异常处理类
    SpringMVC返回一个JSON对象到界面
  • 原文地址:https://www.cnblogs.com/happy-MEdge/p/10498375.html
Copyright © 2020-2023  润新知