拓展Lucas小结
拓展Lucas是解决大组合数取模非质数(尤其是含平方因子的合数)问题的有力工具...
首先对模数质因数分解,把每个质因子单独拎出来处理答案,然后用中国剩余定理(excrt)合并
问题转化为,对于每个质因子p,求$C_{n}^{m}(mod\;p^k)$
把$C_{n}^{m}$展开成$\frac{n!}{m!(n-m)!}$,发现上下的阶乘里,都可能有质因子p,把它们从阶乘里提取出来,额外求出$n!$里p的数量,减掉$m!$和$(n-m)!$里p的数量,再乘回答案里
剩余的部分就是$n!$,$m!$和$(n-m)!$去掉p的部分,因为它们都关于模数$p^k$互质,除以$m!$和$(n-m)!$就可以用exgcd求逆元了
现在剩余的任务就是处理$n!$去掉所有p的剩余部分
比如n=19,p=3,k=2时
剩余$1*2*4*5*7*8*10*11*13*14*16*17*19(mod\;3^2)$
略微变形成$(1*2*4*5*7*8)*(10*11*13*14*16*17)*19(mod\;3^2)$
发现它竟然是一个又一个循环节,循环节长度为$p^k$,而且每一个循环节都是同模的,所以计算出一个循环节的答案,再用快速幂求得所有循环节相乘的答案
对于每个循环节,递归求解即可
每次计算还会剩余长度小于循环节一小段,因为它长度小于$p^k$,暴力计算就行了
最后合并每个质因子的答案即可模数
时间非常玄学,由出题人的毒瘤程度模数决定
代码好长啊
1 namespace exlucas{ 2 ll ans=0,M=1; 3 ll son[10],pw[10]; 4 int num; 5 int excrt_ins(ll A,ll B) 6 { 7 ll a=A,b=B,c=(a-ans%b+b)%b,x,y; 8 ll g=exgcd(M,b,x,y);ll bg=b/g; 9 if(c%g!=0) return -1; 10 //x=x*(c/g)%bg; 11 x=qmul(x,c/g,bg); 12 ans+=x*M,M*=bg,ans=(ans%M+M)%M; 13 return 1; 14 } 15 ll get_mul(ll n,ll p,ll &sum,const ll &mo,int type) 16 { 17 if(n==0) return 1; 18 ll ans=1; 19 for(int i=2;i<=min(n,mo);i++) 20 if(i%p) ans=ans*i%mo; 21 ans=qpow(ans,n/mo,mo); 22 for(int i=2;i<=n%mo;i++) 23 if(i%p) ans=ans*i%mo; 24 sum+=1ll*(n/p)*type; 25 return ans*get_mul(n/p,p,sum,mo,type)%mo; 26 } 27 ll get_C(ll n,ll m,ll p,const ll &mo) 28 { 29 if(m>n) return 0; 30 ll sum=0;ll y; 31 ll nn=get_mul(n,p,sum,mo,1); 32 ll mm=get_mul(m,p,sum,mo,-1); 33 ll nm=get_mul(n-m,p,sum,mo,-1); 34 exgcd(mm,mo,mm,y); 35 mm=(mm%mo+mo)%mo; 36 exgcd(nm,mo,nm,y); 37 nm=(nm%mo+mo)%mo; 38 return nn*mm%mo*nm%mo*qpow(p,sum,mo)%mo; 39 } 40 ll C(ll n,ll m,const ll &mo) 41 { 42 if(m>n) return 0; 43 ll ret=0; 44 for(int i=0;i<num;i++){ 45 ll val=get_C(n,m,son[i],pw[i]); 46 excrt_ins(val,pw[i]); 47 } 48 ret=ans,M=1,ans=0; 49 return ret; 50 } 51 };