hdu 5698 组合数

来源:互联网 发布:人像素描书 知乎 编辑:程序博客网 时间:2024/05/21 17:30

瞬间移动

Problem Description

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

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

Input

多组测试数据。

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

Output

一个整数表示答案

Sample Input
4 5
Sample Output
10

Source

2016"百度之星" - 初赛(Astar Round2B) 

分析:

画几个数之后就可以发现规律了,可以看出是一个倾斜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\n");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\n",ans);    }    return 0;}
当然逆元也有很多种实现方法:

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

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

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

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


0 0
原创粉丝点击