[hdu5698]: 瞬间移动(两种方法求组合数)


welcome to my blog!!!

瞬间移动

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    0    0    0    0

0    1    1    1    1

0    1    2    3    4

0    1    3    6    10

0    1    4  10   20

此时把这个矩阵斜着看 发现好像杨辉三角的规律

在仔细一想 第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;
}


  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值