hdu 5698 组合数
瞬间移动
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\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这题的思想是一致的。