擴展盧卡斯定理(Exlucas)
阿新 • • 發佈:2019-01-02
基本 經典 display code 質數 求解 freopen gist 題目
我們可以繼續遞歸求解,那麽怎麽分組呢
我們可以把每一段的範圍定為\(p^c\)。差不多就這樣吧。
題目鏈接
戳我
前置知識
- 中國剩余定理(crt)或擴展中國剩余定理(excrt)
- 乘法逆元
- 組合數的基本運用
- 擴展歐幾裏得(exgcd)
說實話Lucas真的和這個沒有什麽太大的關系,但是Lucas還是要學學的:戳我
正文
題目是要求:
\[c_n^m mod \ p\]
如果這個p是質數的話那太簡單了,直接Lucas就好了,但問題是現在p不一定是一個質數。
我們令 \(P=\prod {p_i}^{c_i}\)
我們如果知道每個\(c_n^m mod \ p_i^{c_i}\)的值的話就可以根據中國剩余定理求出答案
那我們怎麽求出這個值呢?
我們可以將\(c_n^m\)寫成\(\frac{n!}{m!(n-m)!}\)
現在我們可以處理階乘的模。那麽如何處理階乘的模呢?
舉個經典例子:
\(p=3,n=19,c=2\)時
我們可以吧式子寫成這樣:
\[(19*18*17*16*15*14*13*12*11*10*9*8*7*6*5*4*3*2*1)\]
\[=(19*17*16*14*13*11*10*8*7*5*4*2*1)*3^6*6!\]
我們可以將他分為幾個部分
\[19*(17*16*14*13*11*10)*(8*7*5*4*2*1)*3^6*6!\]
我們會發現對於每一個整的部分如\((8*7*5*4*2*1)\)的模數都是一樣的,於是這一塊我們可以運用快速冪,而剩余的\(19\)我們可以進行暴力。對於\(6!\)
我們可以把每一段的範圍定為\(p^c\)。差不多就這樣吧。
code
// luogu-judger-enable-o2 // luogu-judger-enable-o2 // luogu-judger-enable-o2 #include<bits/stdc++.h> #define rg register #define int long long #define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout); using namespace std; int read(){ int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0'&&c<='9') x=x*10+c-48,c=getchar(); return f*x; } inline void exgcd(int a,int b ,int &x,int &y){ if(!b){x=1,y=0;return;} exgcd(b,a%b,x,y); int t=x; x=y,y=t-(a/b)*y; } inline int inv(int a,int b){ int x,y; return exgcd(a,b,x,y),(x%b+b)%b; } inline int ksm(int a,int b,int p){ int ans=1; while(b){ if(b&1) ans=a*ans%p; a=a*a%p; b>>=1; } return ans%p; } inline int crt(int x,int p,int mod){ return inv(p/mod,mod)*(p/mod)*x; } inline int fac(int x,int a,int b){ if(!x) return 1; int ans=1; for(int i=1;i<=b;i++) if(i%a) ans*=i,ans%=b; ans=ksm(ans,x/b,b); for(int i=1;i<=x%b;i++) if(i%a) ans*=i,ans%=b; return ans*fac(x/a,a,b)%b; } inline int C(int n,int m,int a,int b){ int N=fac(n,a,b),M=fac(m,a,b),Z=fac(n-m,a,b),sum=0; for(int i=n;i;i=i/a) sum+=i/a; for(int i=m;i;i=i/a) sum-=i/a; for(int i=n-m;i;i=i/a) sum-=i/a; return N*ksm(a,sum,b)%b*inv(M,b)%b*inv(Z,b)%b; } inline void exlucas(int n,int m,int p){ int t=p,ans=0; for(int i=2;i*i<=p;i++){ int k=1; while(t%i==0) k*=i,t/=i; ans+=crt(C(n,m,i,k),p,k),ans%=p; } if(t>1) ans+=crt(C(n,m,t,t),p,t),ans%=p; printf("%d",ans%p); } main(){ int n=read(),m=read(),p=read(); exlucas(n,m,p); return 0; }
擴展盧卡斯定理(Exlucas)