题目:点击打开链接
额,,,走方格不是走边。
杨辉三角形,从A出发到每个点的走法数等于它左边点和上边点的走法相加
1 1 1 1
1 2 3 4
1 3 6 10
1 4 10 20
可以看出结果为C(min(n-1,m-1),n+m-2 )
令m+n-2=m, m-1=n, 即c(n, m)=m!/((n-m)!*n!)%N; 再令:a=m!/(m-n)!,b=n!;
所以只需要求 a / b%N;但a,b本身就超过long long了,为了防止溢出,可以(a%N )/ (b%N)%N,但我们发现除法没法这么算,
因为比如9 / 3%14,搞完3%14成0了,b%N经常会变成0,会出错。
所以可以这么处理:利用费马小定理
费马小定理:b^(N - 1)=1(mod N),前提是b是整数,N是质数,b,N互质,显然符合
而a / b=x(mod N),两边关于N互余
两边相乘,得,a * b^(N - 2)=x(mod N);用快速幂处理即可。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
const ll mod=1e9+7;
ll get_pow(ll x, ll n){ //快速幂
ll ans=1;
while(n){
if(n&1){
ans=ans*x%mod;
}
x=x*x%mod;
n>>=1;
}
return (ans+mod)%mod;
}
ll C(ll n,ll m){ //C(n,m),m里选n个。
ll ans,a=1,b=1;
for(ll i=m;i>=m-n+1;i--)
a=a*i%mod;
for(ll i=n;i>1;i--)
b=b*i%mod;
ans=a*get_pow(b,mod-2)%mod;
return ans;
}
int main(){
ll n,m;
cin>>n>>m;
ll ans=C(min(n-1,m-1),m+n-2);
cout<<ans<<endl;
return 0;
}
还有一种处理方法比较类似,求逆元。
可以转化为求a * b'%N,其中b’为%N的乘法逆元。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
const ll mod=1e9+7;
void gcd(ll a,ll b,ll &d,ll &x,ll &y){
if(!b){d=a;x=1;y=0;}
else{ gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
ll inv(ll a,ll n){
ll d,x,y;
gcd(a,n,d,x,y);
return d==1? (x+n)%n:-1;
}
ll C(ll n,ll m){ //C(n,m),m里选n个。
ll ans,a=1,b=1;
for(ll i=m;i>=m-n+1;i--)
a=a*i%mod;
for(ll i=n;i>1;i--)
b=b*i%mod;
ans=a*inv(b,mod)%mod;
return ans;
}
int main(){
ll n,m;
cin>>n>>m;
ll ans=C(min(n-1,m-1),m+n-2);
cout<<ans<<endl;
return 0;
}