Unknown Treasure

On the way to the next secret treasure hiding place, the mathematician discovered a cave unknown to the map. The mathematician entered the cave because it is there. Somewhere deep in the cave, she found a treasure chest with a combination lock and some numbers on it. After quite a research, the mathematician found out that the correct combination to the lock would be obtained by calculating how many ways are there to pick m different apples among n of them and modulo it with M. M is the product of several different primes.
Input
On the first line there is an integer T(T≤20) representing the number of test cases.
Each test case starts with three integers n,m,k (1≤m≤n≤1018, 1≤k≤10) on a line where k is the number of primes. Following on the next line are k different primes p1,…,pk. It is guaranteed that M=p1⋅p2⋯pk≤1018 and pi≤105 for every i∈{1,…,k}.
Output
For each test case output the correct combination on a line.
Samples
Input
1
9 5 2
3 5
Output
6

题意求C(n,m)%M (n,m)<=1e18,M可以分解为若干个素数p1,p2,…的乘积

拓展卢卡斯定理

首先对p进行质因数分解:

		p=∏pi^ki;

所以如果分别求出C(n,m) mod piki,就可以构造出若干个形如C(n,m)=ai mod piki的方程,然后用中国剩余定理即可求解。
题目中已经给出p1,p2,p3…均为素数,所以构造a[i]=C(n,m)%p[i]=X(%p[i])的一系列方程,用中国剩余定理求出X就是最终结果,由于数据较大,需要使用按位乘法防止数据爆longlong.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+7;
ll fac[maxn];
ll inv[maxn];
ll fast_cheng(ll a,ll b,ll mod)
{
	if(b<0)b=(b%mod+mod)%mod;
    a%=mod;
    if(a<b)swap(a,b);
	ll ans = 0;
    while(b){
        if(b & 1) ans = (ans+a)%mod;
        b>>=1;
        a = (a+a)%mod;
    }
    return ans;
}
ll fast_pow(ll a, ll b, ll mod)
{
    ll ans = 1;
    while(b){
        if(b & 1) ans = (ans*a)%mod;
        b>>=1;
        a = (a*a)%mod;
    }
    return ans;
}
ll exgcd(ll a, ll b, ll &x, ll &y) 
{
	if (b == 0) 
	{ 
		x = 1, y = 0; 
		return a; 
	}
	ll ret = exgcd(b, a%b, y, x);
	y -= a / b * x;
	return ret;
}
void init(ll n,ll m,ll mod)
{
    fac[0]=fac[1]=1;
    for (ll i=2;i<=n;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[m]=fast_pow(fac[m],mod-2,mod);
    inv[n-m]=fast_pow(fac[n-m],mod-2,mod);
}
ll com(ll n,ll m,ll mod)
{
    if (m>n) return 0;
    if (m==n) return 1;
    init(n,m,mod);
    return fac[n]*inv[n-m]%mod*inv[m]%mod;
}

ll C(ll n,ll m,ll mod)
{
    if (m==0)
        return 1;
    return fast_cheng(C(n/mod,m/mod,mod),com(n%mod,m%mod,mod),mod);
}
ll p[50];
ll a[50];
ll China(ll a[],ll m[],int hh)
{
    ll M=1,Mi,ans=0;
    ll x,y,d;
    for(int i=1;i<=hh;i++) 
        M*=m[i];
    for(int i=1;i<=hh;i++)
    {
        Mi=M/m[i];
        d=exgcd(Mi,m[i],x,y);
        ans=(ans+fast_cheng(fast_cheng(Mi,x,M),a[i],M))%M;
    }
    if(ans<0) ans+=M;
    return ans;
}
ll n,m,k;
int main()
{
	int T;
	cin>>T;
	int k;
	//cout<<C(3,2,10) ;
	while(T--)
	{
		scanf("%lld %lld %d",&n,&m,&k);
		for(int i=1;i<=k;i++)
			scanf("%lld",&p[i]);
		
		for(int i=1;i<=k;i++)
			a[i]=C(n,m,p[i]);
			//cout<<a[i]<<endl; 
		
		cout<<China(a,p,k)<<endl;
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值