BZOJ2142——乘法逆元+中国剩余定理
2018-04-03 08:48
441 查看
Description
一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E
心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。小E从商店中购买了n件礼物,打算送给m个人
,其中送给第i个人礼物数量为wi。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某
个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模P后的结果。
Input
输入的第一行包含一个正整数P,表示模;
第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;
以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。
Output
若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。
Sample Input
100
4 2
1
2
Sample Output
12
【样例说明】
下面是对样例1的说明。
以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:
1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23
【数据规模和约定】
设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
对于100%的数据,1≤n≤10^9,1≤m≤5,1≤pi^ci≤10^5。
这道题我想了好久才想透,算是比较复杂的一道数论题。
我们先看题意,要我们求总的方案数,这个方案数其实是挺好推的,因为 n个礼物选出总共sum个,sum里又要选a[1]个,sum-a[1]里选a[2]个。所以我们推出答案为: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
)。
这就是我们要求的东西,但是我们要mod数p,如果p是质数,那也十分简单,因为我们直接用Lucas定理求解即可。可这里的p并不是质数。所以我们就得把p分解质因数,然后用中国剩余定理合并(关于怎么合并,我会在接下去讲中国剩余定理的时候介绍)即可。
我们分解质因数,然后用C(n,m)%pi^ki。这里就要用到乘法逆元了:
C(n,m)%pi^ki=n!%pi^ki*inv(m!,pi^ki)*inv(n-m!,pi^ki)。但是如果直接用逆元的话并不可以,因为有可能在这些阶乘中出现取模的数,这样的话就直接变为0了。所以我们要把这些数提取出来。比如6!mod3=1*2*4*5*3^2*1*2,我们对于每一个有3的提出3,然后就可以用逆元了。(注意这里的逆元必须用扩展欧几里得,由于费马小定理必须满足pi为质数或伪素数,但这里是pi^ki)
这里我们要介绍用扩展欧几里得求逆元。逆元的公式为ax≡1(mod m),则我们可以展开即ax-my=1,我们将y取负,即为ax+bm=1,这就可以用扩展欧几里得了。
为什么可以用中国剩余定理合并呢?我们要先了解中国剩余定理。
中国剩余定理是求一次同余方程的定理,对于方程:
x≡a1(mod m1), x≡a2(mod m2) …… x≡an(mod mn),我们就可以求出x。
若n1满足n1≡a1(mod m1),且m2~mn|n1,而n2满足n2≡1(mod m2),且除m2的数正处n2……一次类推,这n1到nn全部加起来就是x的一个解。而我们用M=∏mi,则x的解集为{x|x=Mk+∑ni,k∈Z}。
我们用Mi=M/mi,则ni=ai*Mi*inv(Mi,mi),因为这样ni mod mi=ai。
我们可以用一个式子表示:x≡∑ai*Mi*inv(Mi,mi)(mod M)。
这样我们发现,M即为题目中的P,而x也就是我们要求的答案,所以最后mod出来的东西就是中间那一坨,也就是∑ai*Mi*(Mi关于mi的逆元)。
一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E
心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。小E从商店中购买了n件礼物,打算送给m个人
,其中送给第i个人礼物数量为wi。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某
个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模P后的结果。
Input
输入的第一行包含一个正整数P,表示模;
第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;
以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。
Output
若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。
Sample Input
100
4 2
1
2
Sample Output
12
【样例说明】
下面是对样例1的说明。
以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:
1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23
【数据规模和约定】
设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
对于100%的数据,1≤n≤10^9,1≤m≤5,1≤pi^ci≤10^5。
这道题我想了好久才想透,算是比较复杂的一道数论题。
我们先看题意,要我们求总的方案数,这个方案数其实是挺好推的,因为 n个礼物选出总共sum个,sum里又要选a[1]个,sum-a[1]里选a[2]个。所以我们推出答案为: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
)。
这就是我们要求的东西,但是我们要mod数p,如果p是质数,那也十分简单,因为我们直接用Lucas定理求解即可。可这里的p并不是质数。所以我们就得把p分解质因数,然后用中国剩余定理合并(关于怎么合并,我会在接下去讲中国剩余定理的时候介绍)即可。
我们分解质因数,然后用C(n,m)%pi^ki。这里就要用到乘法逆元了:
C(n,m)%pi^ki=n!%pi^ki*inv(m!,pi^ki)*inv(n-m!,pi^ki)。但是如果直接用逆元的话并不可以,因为有可能在这些阶乘中出现取模的数,这样的话就直接变为0了。所以我们要把这些数提取出来。比如6!mod3=1*2*4*5*3^2*1*2,我们对于每一个有3的提出3,然后就可以用逆元了。(注意这里的逆元必须用扩展欧几里得,由于费马小定理必须满足pi为质数或伪素数,但这里是pi^ki)
这里我们要介绍用扩展欧几里得求逆元。逆元的公式为ax≡1(mod m),则我们可以展开即ax-my=1,我们将y取负,即为ax+bm=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 inverse(ll a,ll b){ ll x1,y1,gcd; exgcd(a,b,x1,y1,gcd); return (x1%b+b)%b; }
为什么可以用中国剩余定理合并呢?我们要先了解中国剩余定理。
中国剩余定理是求一次同余方程的定理,对于方程:
x≡a1(mod m1), x≡a2(mod m2) …… x≡an(mod mn),我们就可以求出x。
若n1满足n1≡a1(mod m1),且m2~mn|n1,而n2满足n2≡1(mod m2),且除m2的数正处n2……一次类推,这n1到nn全部加起来就是x的一个解。而我们用M=∏mi,则x的解集为{x|x=Mk+∑ni,k∈Z}。
我们用Mi=M/mi,则ni=ai*Mi*inv(Mi,mi),因为这样ni mod mi=ai。
我们可以用一个式子表示:x≡∑ai*Mi*inv(Mi,mi)(mod M)。
这样我们发现,M即为题目中的P,而x也就是我们要求的答案,所以最后mod出来的东西就是中间那一坨,也就是∑ai*Mi*(Mi关于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; }
相关文章推荐
- 【bzoj2142】礼物 组合数学+中国剩余定理
- 【bzoj2142】礼物 扩展Lucas定理+中国剩余定理
- [BZOJ2142]礼物-扩展lucas定理-中国剩余定理
- [bzoj2142]礼物(扩展lucas定理+中国剩余定理)
- 【BZOJ2142】礼物(扩展lucas定理,中国剩余定理合并方程)
- [BZOJ2142] 礼物 - Lucas定理及扩展 - 中国剩余定理 - 扩展欧几里得算法
- BZOJ 1951 费马小定理 + Lucas定理 + 乘法逆元 + 中国剩余定理 + 快速幂
- [BZOJ2142]-扩展Lucas+中国剩余定理
- [BZOJ2142]礼物(扩展Lucas定理+中国剩余定理)
- 【BZOJ1951】[中国剩余定理][SDOI2010]古代猪文
- bzoj 1951: [Sdoi2010]古代猪文 (Lucas定理+中国剩余定理)
- bzoj 2445 最大团(阶乘取模+中国剩余定理CRT)
- BZOJ 1951 古代猪文 鲁卡斯定理+费马小定理+中国剩余定理
- [BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】
- [BZOJ1951][SDOI2010]古代猪文(Lucas定理+中国剩余定理)
- BZOJ 3782 上学路线 ——动态规划 Lucas定理 中国剩余定理
- bzoj 1951(Lucas定理+中国剩余定理)
- [BZOJ 1951][Sdoi2010]古代猪文:Lucas定理|中国剩余定理|费马小定理|扩展欧几里得
- BZOJ-1407 Savage 枚举+拓展欧几里得(+中国剩余定理??)
- BZOJ_P1951&Codevs_P1830 [SDOI2010]古代猪文(Lucas定理+扩展欧几里得+中国剩余定理)