BZOJ2142——Multiplication Inverse + Chinese Remainder Theorem

Description

The annual Christmas is coming soon. Every Christmas, little E receives many gifts, and of course he also gives many gifts. Different characters have different importance in Xiao E's mind. The heavier the weight in Xiao E's heart, the more gifts they will receive. Xiao E buys n gifts from the store and intends to give them to m people, and the number of gifts to the ith person is wi. Please help to count the number of options for giving gifts (two options are considered different if and only if there is a person who received a different gift in the two options). Since the number of solutions may be large, you only need to output the modulo P result.
Input

The first line of the input contains a positive integer P, which means modulo;
the second line contains two integers n and m, which represent the number of gifts that little E bought from the store and the number of people who received gifts;
the following m lines each contain only one A positive integer wi, indicating the number of gifts that small E wants to give to the ith person.
Output

If there is no feasible solution, output "Impossible", otherwise output an integer representing the number of solutions modulo P.

Sample Input

100

4 2

1

2

Sample Output

12

【Example description】

The following is an illustration of Example 1.

Separated by "/", before and after "/" respectively indicate the gift number for the first person and the second person. The details of the 12 programs are as follows:

1/23 1/24 1/34

2/13 2/14 2/34

3/12 3/14 3/24

4/12 4/13 4/23

【Data scale and convention】

Let P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct, and pi is a prime number.

For 100% data, 1≤n≤10^9, 1≤m≤5, 1≤pi^ci≤10^5.


I thought about this question for a long time before I figured it out. It is a relatively complex number thesis.
Let's look at the meaning of the question first and ask us to ask for the total number of solutions. This number of solutions is actually quite easy to push, because a total of sum is selected from n gifts, and a[1] must be selected in the sum, sum-a[1 ] to select a[2]. So we deduce the answer as: C(n,sum)*C(sum,a[1])*C(sum-a[1],a[2])*C(sum-a[1]-a[2 ],a[3])...*C(sum-∑a[1~n-1],a[n]).
This is what we ask for, but we want to mod the number p, and if p is a prime number, that's pretty easy, because we can just solve it with Lucas's theorem. But p here is not a prime number. So we have to decompose p into prime factors, and then use the Chinese remainder theorem to combine (for how to combine, I will introduce it when I talk about the Chinese remainder theorem next).
We decompose the prime factors, then use C(n,m)%pi^ki. This is where the multiplicative inverse is used:
C(n,m)%pi^ki=n!%pi^ki*inv(m!,pi^ki)*inv(nm!,pi^ki). But if you use the inverse element directly, it is not possible, because there may be modulo numbers in these factorials, so it will become 0 directly. So we have to extract these numbers. For example, 6!mod3=1*2*4*5*3^2*1*2, we propose 3 for every 3, and then we can use the inverse element. (Note that the inverse element here must be extended Euclid, because Fermat's little theorem must satisfy that pi is a prime number or a pseudo-prime number, but here is pi^ki)
Here we introduce the use of extended Euclidean inversion. The formula of the inverse element is ax≡1 (mod m), then we can expand it, that is, ax-my=1, and we negate y, that is, ax+bm=1, which can be obtained by extended Euclidean.
//扩展欧几里得求逆元
void exgcd(ll a,ll b,ll &x,ll &y,ll &gcd){
    if(b==0){
        x=1;y=0;gcd=a;return;
    }
    exgcd(b,a%b,y,x,gcd);
    y=y-a/b*x;
}
ll inverse(ll a,ll b){
    ll x1,y1,gcd;
    exgcd(a,b,x1,y1,gcd);
    return (x1%b+b)%b;
}
Why can it be combined using the Chinese remainder theorem? We must first understand the Chinese remainder theorem.
The Chinese remainder theorem is a theorem for finding a first degree congruence equation, for the equation:
x≡a1(mod m1), x≡a2(mod m2) …… x≡an(mod mn), we can find x.
If n1 satisfies n1≡a1(mod m1), and m2~mn|n1, and n2 satisfies n2≡1(mod m2), and the number divided by m2 is at n2... an analogy, the sum of all n1 to nn is a solution to x. And we use M=∏mi, then the solution set of x is {x|x=Mk+∑ni,k∈Z}.
We use Mi=M/mi, then ni=ai*Mi*inv(Mi,mi), because ni mod mi=ai.
We can express it with a formula: x≡∑ai*Mi*inv(Mi,mi)(mod M).
In this way, we find that M is the P in the question, and x is the answer we ask for, so the final mod comes out of the middle lump, which is ∑ai*Mi* (Mi's inverse element about mi).
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define pa pair<ll,ll> 
#define fi first
#define se second
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll p,n,m,sum,cnt,ans,w[10005],a[10005];
struct node{
    ll p,num,t;
}F[10005];
void fac(ll x){
    for(ll i=2;i*i<=x;i++){
        if(x%i!=0) continue; 
        F[++cnt].p=i;F[cnt].num=1;
        while(x%i==0){x/=i;F[cnt].t++;F[cnt].num*=i;}
    }
    if(x>1){
        F[++cnt]=(node){x,x,1};
    }
}
void exgcd(ll a,ll b,ll &x,ll &y,ll &gcd){
    if(b==0){
        x=1;y=0;gcd=a;return;
    }
    exgcd(b,a%b,y,x,gcd);
    y=y-a/b*x;
}
ll reverse(ll a,ll b){
    ll x1,y1,gcd;
    exgcd(a,b,x1,y1,gcd);
    return (x1%b+b)%b;
}
ll pows(ll a,ll b,ll md){
    ll base=1;
    while(b){
        if(b&1) base=(base*a)%md;
        a=(a*a)%md;b/=2;
    }
    return base%md;
}
pa go(ll pl,ll x){
    if(x==0) return mp(0,1);
    ll u=x/F[pl].p,v=x/F[pl].num,res=1;
    if(v){
        for(ll i=2;i<F[pl].num;i++)if(i%F[pl].p!=0) res=(res*1ll*i)%F[pl].num;
        res=pows(res,v,F[pl].num);
    }
    for(ll i=v*F[pl].num+1;i<=x;i++) if(i%F[pl].p!=0) res=res*1ll*i%p;
    pa tmp=go(pl,u);
    return mp(u+tmp.fi,res*tmp.se%p);
}
ll calc(ll pl,ll x,ll y){
    if(x<y) return 0;
    pa a=go(pl,x),b=go(pl,y),c=go(pl,x-y);
    return pows(F[pl].p,a.fi-b.fi-c.fi,F[pl].num)*a.se%F[pl].num*reverse(b.se,F[pl].num)%F[pl].num*reverse(c.se,F[pl].num)%F[pl].num;
}
ll CRT(){
    ll x=0,y,gcd;
    for(ll i=1;i<=cnt;i++){
        ll r=p/F[i].num;
        exgcd(F[i].num,r,gcd,y,gcd);
        x=(x+r*a[i]*y)%p;
    }
    return (x+p)%p;
}
ll work(ll u,ll v){
    for(ll i=1;i<=cnt;i++)
      a[i]=calc(i,u,v);
    return CRT();
}
int main()
{
    p=read();m=read();n=read();
    for(ll i=1;i<=n;i++) w[i]=read(),sum+=w[i];
    if(sum>m){
        puts("Impossible");return 0;
    }
    fac(p);
    ans=work(m,sum)%p;
    for(ll i=1;i<=n;i++){
        ans=ans*work(sum,w[i])%p;
        sum-=w[i];
    }
    printf("%lld",ans);
    return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325565826&siteId=291194637