「一本通 6.6 练习 8」礼物
Description
Input
Output
若不存在可行方案,则输出
Impossible
,否则输出一个整数,表示模 P 后的方案数。
Sample Input
100 4 2 1 2
Sample Output
12
Sample Explaination
12 种方案详情如下: {1}{2,3},{1}{2,4},{1}{3,4},{2}{1,3},{2}{1,4},{2}{3,4},{3}{1,2},{3}{1,4},{3}{2,4},{4}{1,2},{4}{1,3},{4}{2,3}
Hint
最近一直在做CRT,乍一看还以为又是一个水题,实际上并没有那么简单。
是时候来总结一下中国剩余定理CRT,并且开启奇妙的扩展卢卡斯定理ex_lucas了。
审题,题意很简单,要求 Π(i=1->m)C(n-∑(j=1->i-1)wj,wj)%P
组合数取模,很容易想到卢卡斯定理。
然而根据题意,P并不一定是质数,普通卢卡斯定理只适用于质数。
怎么办呢?那么你觉得我所说的ex_lucas是干什么用的呢?
如果我们把P质因数分解,不就得到了质数吗?
我们只需要求出ans%piki 然后再CRT解关于它们的余数方程,就好了呀。
简单介绍CRT:
对于余数方程xΞai(mod pi) 其中pi彼此互质。
设P=∏pi,则最小自然数解x=(∑P/pi*ai*si)%P;其中si表示方程(P/pi)*x+pi*y=1的解。
证明略。而对于pi不互质的情况需要ex_CRT,十分恶心不再这里介绍了。
1 //tim[i]即为pi,aans[i]即为a[i],prr即为P 2 int CRT(){ 3 int ans=0,x,y; 4 for(int i=1;i<=nom;++i){ 5 ex_gcd(prr/tim[i],tim[i],x,y); 6 x=(x%tim[i]+tim[i])%tim[i]; 7 ans=(ans+prr/tim[i]*aans[i]%prr*x%prr)%prr; 8 } 9 return ans; 10 }
接下来我们继续考虑求ans%piki,下一个问题还是组合数如何对非质数取模。
然而现在这个非质数不再是普通的合数了,而是一个质数的幂,即要求C(n,m)%pk。
那么过程中求阶乘时不能很轻易地求出它的逆元了,因为它本身就可能含有p这个因子。
那还不简单?把阶乘的因子p都提出来不就好了嘛?
则C(n,m)%pk=(n! / pk1)*inv(m! / pk2)*inv((n-m)! / pk3)*pk1+k2+k3
现在inv内部的东西与p互质了,可以求逆元了。
接下来再来考虑如何求(n! / pk1)%pk,除个pk1不是什么难事,先讨论n!%pk,如22!%32
=1×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×19×20×21×22
提出含p=3的项
=37×(1×2×3×4×5×6×7)×(1×2×4×5×7×8×10×11×13×14×16×17×19×20×22)
蓝色部分:是37,即pn/p,快速幂。
绿色部分:是7!,即(n/p)!,递归解决。
红色部分:是与p互质的数的乘积,其中以pk为分段可以发现前两各完整的段里有
(1×2×4×5×7×8)Ξ(10×11×13×14×16×17) (mod pk)
这样就有了一个循环节,快速幂解决。多余部分暴力乘出来就可以。
1 int fac(int n,int p,int pt,int ans=1){ 2 if(!n)return 1; 3 for(int i=1;i<pt;++i)if(i%p)ans=ans*i%pt; 4 ans=pow(ans,n/pt,pt); 5 for(int i=1;i<=n%pt;++i)if(i%p)ans=ans*i%pt; 6 return ans*fac(n/p,p,pt)%pt; 7 }
逐步回代,解决啦。
1 #include<cstdio> 2 #include<cmath> 3 #define int long long 4 using namespace std; 5 int m,pr,sqr,modd[100005],tim[100005],aans[100005],p[100005],nop,num[6],nom,lef[6],prr;bool np[100005]; 6 int pow(int b,int t,int p,int ans=1){ 7 while(t){ 8 if(t&1)ans=ans*b%p; 9 b=b*b%p; 10 t/=2; 11 } 12 return ans; 13 } 14 int fac(int n,int p,int pt,int ans=1){ 15 if(!n)return 1; 16 for(int i=1;i<pt;++i)if(i%p)ans=ans*i%pt; 17 ans=pow(ans,n/pt,pt); 18 for(int i=1;i<=n%pt;++i)if(i%p)ans=ans*i%pt; 19 return ans*fac(n/p,p,pt)%pt; 20 } 21 void ex_gcd(int a,int b,int &x,int &y){ 22 if(!b){x=1;y=0;return;} 23 ex_gcd(b,a%b,x,y); 24 int res=x;x=y;y=res-a/b*y; 25 } 26 int inv(int n,int p){ 27 int x,y; 28 ex_gcd(n,p,x,y); 29 return (x%p+p)%p; 30 } 31 int c(int b,int t,int p,int pt){ 32 if(b<t)return 0;int cnt=0; 33 for(int i=b;i;i/=p)cnt+=i/p; 34 for(int i=t;i;i/=p)cnt-=i/p; 35 for(int i=b-t;i;i/=p)cnt-=i/p; 36 return fac(b,p,pt)*inv(fac(t,p,pt),pt)%pt*inv(fac(b-t,p,pt),pt)%pt*pow(p,cnt,pt)%pt; 37 } 38 int CRT(){ 39 int ans=0,x,y; 40 for(int i=1;i<=nom;++i){ 41 ex_gcd(prr/tim[i],tim[i],x,y); 42 x=(x%tim[i]+tim[i])%tim[i]; 43 ans=(ans+prr/tim[i]*aans[i]%prr*x%prr)%prr; 44 } 45 return ans; 46 } 47 int ex_lucas(){ 48 for(int i=1;i<=nop;++i){ 49 if(pr%p[i]==0)modd[++nom]=p[i],tim[nom]=1; 50 while(pr%p[i]==0)pr/=p[i],tim[nom]*=p[i]; 51 } 52 for(int ii=1;ii<=nom;++ii){ 53 aans[ii]=1; 54 for(int i=1;i<=m;++i)aans[ii]=(aans[ii]*c(lef[i],num[i],modd[ii],tim[ii]))%tim[ii]; 55 } 56 return CRT(); 57 } 58 signed main(){ 59 scanf("%lld%lld%lld",&pr,&lef[1],&m);prr=pr; 60 for(int i=1;i<=m;++i)scanf("%lld",&num[i]),lef[i+1]=lef[i]-num[i]; 61 if(lef[m+1]<0){puts("Impossible");return 0;} 62 for(int i=2;i<=100000;++i){ 63 if(!np[i])p[++nop]=i; 64 for(int j=1;j<=nop&&i*p[j]<=100000;++j) 65 if(i%p[j])np[i*p[j]]=1; 66 else{np[i*p[j]]=1;break;} 67 } 68 printf("%lld\n",ex_lucas()); 69 }