1. 程式人生 > >[hdu5698]: 瞬間移動(兩種方法求組合數)

[hdu5698]: 瞬間移動(兩種方法求組合數)

瞬間移動

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1178    Accepted Submission(s): 583


Problem Description 有一個無限大的矩形,初始時你在左上角(即第一行第一列),每次你都可以選擇一個右下方格子,並瞬移過去(如從下圖中的紅色格子能直接瞬移到藍色格子),求到第n行第m列的格子有幾種方案,答案對1000000007取模。


Input 多組測試資料。

兩個整數n,m(2n,m100000)

Output 一個整數表示答案
Sample Input 4 5
Sample Output 10
Source
Recommend wange2014

自己畫出前面幾行幾列 找個規律出來

類似這個樣子

0    0000

01111

01234

013610

0141020

此時把這個矩陣斜著看 發現好像楊輝三角的規律

在仔細一想 第n行m列 就等於C(n+m-4,n-2) 或者C(n+m-4,m-2)相等的

於是求出這個組合數就可以了啊

今天學了求這個組合數(乘法逆元)的兩種方法:

1.擴充套件歐幾里得求乘法逆元

2.費馬小定理+快速冪

程式碼如下:

1.根據二項式定理直接算C  除的時候不用除直接乘上逆元即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define mod 1000000007
#define ll long long 
using namespace std;
int x,y;
void exgcd(int a,int b){
	if(b==0) {x=1;y=0;}
	else {
		exgcd(b,a%b);
		int t=x;
		x=y;
		y=t-(a/b)*y;
	}
}
//求C(n+m-4,n-2) 二項式定理 
int main(){
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF){
		ll ans=1;
		for(int i=1;i<=n+m-4;i++)
			ans=ans*i%mod;
		for(int i=1;i<=n-2;i++){
			exgcd(i,mod);
			x=(x%mod+mod)%mod;//求逆元 
			ans=(ans*x)%mod;//乘上逆元 
		}
		for(int i=1;i<=m-2;i++){//n+m+4-(n-2)=m-2
			exgcd(i,mod);
			x=(x%mod+mod)%mod;
			ans=(ans*x)%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
} 
2.費馬小定理+快速冪

用快速冪+費馬小定理求組合數 
 1).擴充套件歐幾里德:b*x+p*y=1 有解,x就是所求
 2).費馬小定理:b^(p-1)=1(mod p),故 b*b^(p-2)=1(mod p),所以x=b^(p-2)

這樣的方法可以算出逆元的另一種求法為:x=b^(p-2) 

前提是p是質數 否則費馬小定理就不成立

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long 
#define mod 1000000007
using namespace std;
ll fac[200005];
void pre(){//預處理乘方
	fac[1]=1;
	for(int i=2;i<=200000;i++)
		fac[i]=fac[i-1]*i%mod; 
}
ll quick_pow(ll a,ll b){
	ll ans=1;
	for(ll i=b;i;i>>=1,a=(a*a)%mod)
		if(i&1) ans=(ans*a)%mod;
	return ans;
} 
int main(){
	pre();
	int n,m;
//	for(int i=1;i<=100;i++)
//		printf("%d ",fac[i]);
	while(scanf("%d%d",&n,&m)!=EOF){
		ll ans;
		ans=fac[n+m-4];	
		//printf("%lld\n",ans);
		ans=ans*quick_pow(fac[n-2],mod-2)%mod;
		//printf("%lld\n",quick_pow(fac[n-2],mod-2));
		ans=ans*quick_pow(fac[m-2],mod-2)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}