FOJ 2020 组合(组合数取素数摸模板:Lucas)

79 篇文章 0 订阅
31 篇文章 0 订阅


Link:http://acm.fzu.edu.cn/problem.php?pid=2020


Problem 2020 组合

Accept: 776    Submit: 1849
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

给出组合数C(n,m), 表示从n个元素中选出m个元素的方案数。例如C(5,2) = 10, C(4,2) = 6.可是当n,m比较大的时候,C(n,m)很大!于是xiaobo希望你输出 C(n,m) mod p的值!

 Input

输入数据第一行是一个正整数T,表示数据组数 (T <= 100) 接下来是T组数据,每组数据有3个正整数 n, m, p (1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素数)

 Output

对于每组数据,输出一个正整数,表示C(n,m) mod p的结果。

 Sample Input

25 2 35 2 61

 Sample Output

110

 Source

FOJ有奖月赛-2011年04月(校赛热身赛)



AC code:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<vector>
#define LL long long
#define MAXN 1000010
using namespace std;
const  int N=20;//模方程数 
LL a[N],mod[N];
/*LL mul(LL a,LL b,LL mod)//a*b%mod
{
	LL ans=0;
	while(b){
		if(b&1)
			ans=(ans+a)%mod;
		b>>=1;
		a=(a+a)%mod;
	}
	return ans;
}
*/
LL quick_mod(LL a,LL b,LL m)//a^b%m 
{
	LL ans=1;
	a%=m;
	while(b)
	{
		if(b&1)
		{
			ans=ans*a%m;
		}			
		b>>=1;
		a=a*a%m;
	}
	return ans;
}

LL getC(LL n,LL m,int cur)//C(n,m)%mod[cur]
{
	LL p=mod[cur];
	if(m>n)
		return 0;
	if(m>n-m)
		m=n-m;
	LL ans=1;
	for(int i=1;i<=m;i++)
	{
		LL a=(n+i-m)%p;
		LL b=i%p;
		//ans=mul(ans,mul(a,quick_mod(b,p-2,p),p),p);//p为素数,i对p的逆元可以不用扩张欧几里得进行求解  re=i^(P-2) 
		//ans=(ans*(a*quick_mod(b,p-2,p))%p)%p;
		ans = ans * (a * quick_mod(b, p-2,p) % p) % p;  
	}
	return ans; 
}

LL Lucas(LL n,LL k,int cur)//求C(n,k)%mod[cur] 
{
	LL p=mod[cur];
	if(k==0)
		return 1%p;
	//return getC(n%p,k%p,cur)*Lucas(n/p,k/p,cur)%p;
	return getC(n % p, k % p,cur) * Lucas(n / p, k / p,cur) % p;  
}

/*void extend_Euclid(LL a,LL b,LL &x,LL &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	extend_Euclid(b,a%b,x,y);
	LL tmp=x;
	x=y;
	y=tmp-a/b*y;
}

LL CRT(LL a[],LL m[],int k)//求C(n,m)%M,其中M=(m0*m2*…*m(k-1)),mi为素数,则先用a[i]存储模方程C(n,m)%mi,
{                           //m[]存储所有素数因子mi,k表示总共有k个模方程,返回C(n,m)%M的值 
	LL M=1;
	LL ans=0;
	for(int i=0;i<k;i++)
		M*=mod[i];
	for(int i=0;i<k;i++)
	{
		LL x,y,tmp;
		LL Mi=M/m[i];
		extend_Euclid(Mi,m[i],x,y);
		if(x<0)
		{
			x=-x;
			tmp=mul(Mi,x,M);
			tmp=mul(tmp,a[i],M);
			tmp=-tmp;
		}
		else
		{
			tmp=mul(Mi,x,M);
			tmp=mul(tmp,a[i],M);
		}
		ans=(ans+tmp)%M;
	}
	while(ans<0)
		ans+=M;
	return ans;
}*/

int main()
{
	//freopen("D:\\in.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		LL n,m;
		int k;
		scanf("%lld%lld",&n,&m);
		k=1;
		for(int i=0;i<k;i++)
			scanf("%lld",&mod[i]);
		for(int i=0;i<k;i++)
			a[i]=Lucas(n,m,i)%mod[i];
		printf("%I64d\n",a[0]);
	}
  	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林下的码路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值