题目描述

给出\(n\)\(m\)以及\(k\)个质数,设\(M\)为$\prod^{k}_{i=1}p_i \(,\)p_i\(互不相同,求\)C(n,m)%M$

Input

第一行一个\(T\)表示数据组数。

对于每组数据第一行\(n\),\(m\),\(k\)

第二行\(k\)个质数,其互不相同。

\(1≤m≤n≤10^{18}\)

\(1≤k≤10\)

\(p_i≤10^5\)

Output

对于每组数据输出答案。

Sample Input

1
9 5 2
3 5

Sample Output

6

扩展卢卡斯定理即可。

首先,对于一个模数为质数的的组合数,我们可以直接利用卢卡斯定理。

\(C(n,m)\%p=C(n/p,m/p)*C(n\%p,m\%p)\%p\)

可是题目中给出是一个合数,不是质数,那咋办呀?

我们发现模数最多由不超过\(10\)个不同的质数组成,对于一个\(C(n,m)\),我们可以直接求出对于每个质数的取模结果,设第\(i\)个结果为\(res_i\)

则我们就有一个方程组
\[\begin{cases}C(n,m)=res_1\%p_1 \\C(n,m)=res_2\%p_2 \\...... \\C(n,m)=res_k\%p_k\end{cases}\]
我们直接利用中国剩余定理合并即可。

#include <cstdio>
#include <iostream>

using namespace std;

#define int long long
#define reg register
#define clr(a,b) memset(a,b,sizeof a)
#define Mod(x) (x>=mod)&&(x-=mod)
#define abs(a) ((a)<0?-(a):(a))
#define debug(x) cerr<<#x<<"="<<x<<endl;
#define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl;
#define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
#pragma GCC optimize(2)

inline int Read(void) {
    int res=0,f=1;
    char c;
    while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),c>=48&&c<=57);
    return f?res:-res;
}

template<class T>inline bool Min(T &a, T const&b) {
    return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a, T const&b) {
    return a<b?a=b,1:0;
}
const int N=25,M=1e5+5;

int n,m,k,res[N],mod[N],a[N],pos[N];

int Exgcd(int a, int b, int &x, int &y) {
    if(!b) {
        x=1,y=0;
        return a;
    }
    int g=Exgcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}

inline int Excrt(void) {
    int M=mod[1],ans=res[1],x,y;
    rep(i,2,k) {
        int g=Exgcd(M,mod[i],x,y);
        if((res[i]-ans)%g)return -1;
        x*=(res[i]-ans)/g,y=mod[i]/g,x=(x%y+y)%y;
        ans=M*x+ans,M=M/g*mod[i],ans%=M;
    }
    int z=(ans%M+M)%M;
    return z;
}

int Mod;

int Pow(int x,int y) {
    int res=1;
    while(y) {
        if(y&1)res=(res*x)%Mod;
        x=(x*x)%Mod,y>>=1;
    }
    return res;
}

inline int C(int x,int y) {
    if(y>x)return 0;
    int up=1,down=1;
    rep(i,x-y+1,x)up=up*i%Mod;
    rep(i,1,y)down=down*i%Mod;
    return up*Pow(down,Mod-2)%Mod;
}

int Lucas(int x,int y) {
    if(!y)return 1;
    return Lucas(x/Mod,y/Mod)*C(x%Mod,y%Mod)%Mod;
}

inline void _main(void) {
    int T=Read(),Case=0;
    while(T--) {
        n=Read(),m=Read(),k=Read();
        rep(i,1,k) Mod=mod[i]=Read(),res[i]=Lucas(n,m);
        printf("%lld\n",Excrt());
    }
}

signed main() {
    _main();
    return 0;
}
02-13 08:39