LightOJ - 1067 数论<100000左右的组合数取模求法《逆元》>

题目链接: LightOJ - 1067   


对于组合数取模的问题----数值10e6且询问的次数很多的情况--我们可以通过打表降低时间复杂度


先说一下:C(n,m)= n!/(m!*(n-m)!)-----

C(n ,m)=(n*(n-1)*....*(n-m+1))/(1*2*..........*m)=(n*(n-1)*(n-2)*....1)/((n-m)*(n-m-1)*...*1)/(1*2*...*m)= N! / M! /(N-M)!


打一个N!取模表--再打一个N!对模的逆元表--

对于(A/B)mod C  的问题,直接(A mod C)/ (B mod C)是错误的:   ( 16/ 8 ) % 4 == 2    而  (16%4)/(8%4)无意义--

逆元  AB==1(mod)C

A关于C的逆元为B-

B关于C的逆元为A-

即关于对C取模时---A*B=1;

B=1/A;

当我们求S/A(mod)C时---我们就可以求S*B(mod)C了---


求逆元可以用拓展欧几里德--

在此题还可以用费马小定理

假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

 a(p-1)≡1(mod p)所以--- a * a(p-2)≡1(mod p)----即 a 关于 P 的逆元为  *a(p-2)  然后快速幂就行了...


用费马小定理+快速幂在此题时间上有优势:



拓展欧几里德求逆元:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define MA 1000100
#define mod 1000003
LL pri[MA];
LL ni[MA],ans;
LL extend(LL a,LL b,LL &x,LL &y)
{
	if (b==0)
	{
		x=1;y=0;
		return a;
	}
	else
	{
		LL t=extend(b,a%b,y,x);
		y-=x*(a/b);
		return t;
	}
}
void s()
{
	pri[0]=1;LL x,y;
	ni[0]=1;
	for (int i=1;i<MA;i++)
	{
		pri[i]=pri[i-1]*i%mod;
		extend(pri[i],mod,x,y);
		ni[i]=(x%mod+mod)%mod;
	}
}
int main()
{
	s();
	int t,n,k,ca=1;scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&k);
		ans=((pri[n]*ni[k]%mod)*ni[n-k])%mod;
		printf("Case %d: %lld\n",ca++,ans);
	}
	return 0;
}


费马小定理+快速幂求逆元:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define MA 1000100
#define mod 1000003
LL pri[MA];
LL ni[MA],ans;
LL pp(LL xx,int k)
{
	LL lp=1,huan=xx;
	while (k)
	{
		if (k%2)
			lp=(lp*huan)%mod;
		huan=(huan*huan)%mod;
		k/=2;
	}
	return lp;
}
void s()
{
	pri[0]=1;LL x,y;
	ni[0]=1;
	for (int i=1;i<MA;i++)
	{
		pri[i]=pri[i-1]*i%mod;
		ni[i]=pp(pri[i],mod-2);
	}
}
int main()
{
	s();
	int t,n,k,ca=1;scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&k);
		ans=((pri[n]*ni[k]%mod)*ni[n-k])%mod;
		printf("Case %d: %lld\n",ca++,ans);
	}
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值