[學習筆記]擴展LUCAS定理
阿新 • • 發佈:2018-12-01
bool ref eight tps pro 質因子 calc sign 之一
(CRT可以合並的原因是,我們可以求出滿足這些同余方程的通解。發現這些解mod lcm都是同一個數x。$C_n^m$一定是這些個解之一,不管是哪一個,$mod\space lcm$即mod p都是x。我們也就求出了答案)
$\frac{n!}{m!(n-m)!} = a_i\space mod \space pk$
提完質數3之後,變成:
$=(1*2*3*4*5*6)*3^2*(1*2*4*5*7*8*10*11*13*14*16*17*19)$
前面直接遞歸下去處理。後面的每一項,都可以%pk處理。
(不論分母還是分子位置,如果是分母位置,因為存在逆元,
10*inv =1 mod 9
1*inv = 1 mod 9
這兩個inv顯然是同一個inv.所以把所有項%pk沒有問題
)
然後變成:
$=(1*2*3*4*5*6)*3^2*(1*2*4*5*7*8)^2*1)$
後面那個1是多出來的19
其實pk長度的循環節有n/pk個。直接算。剩下(例如這裏的19),個數少於pk,直接算。
(所以,擴展LUCAS的重要適用條件是,$p_i^{q_i}$不能太大(1e5左右))
遞歸算出來即可。
對於分母位置的兩個階乘,算出來結果之後,再處理inv
(這裏可以先乘完之後再找inv,不用一邊找inv一邊乘。)
(註意inv處理要用exgcd,不保證質數,不能用費馬)
(CRT可以不用保存結果,Mi=p/pk 可以一次到位)
可以先做這個題[SDOI2010]古代豬文
此算法和LUCAS定理沒有半毛錢關系。
【模板】擴展盧卡斯
不保證P是質數。
$C_n^m=\frac{n!}{m!(n-m)!}$
麻煩的是分母。
如果互質就有逆元了。
所以可以考慮把分子分母不互質的數單獨提出來處理。
然鵝P太一般,直接處理要考慮的東西太多。
我們不妨令$p=p_1^{q_1}*p_2^{q_2}*...*p_k^{q_k}$
對每一個$p_i^{q_i}$分別求解(不妨叫這個數為$pk$)(這樣會容易很多)
即求ai滿足:$\frac{n!}{m!(n-m)!} = a_i\space mod \space pk$
然後可以$CRT$合並
(CRT可以合並的原因是,我們可以求出滿足這些同余方程的通解。發現這些解mod lcm都是同一個數x。$C_n^m$一定是這些個解之一,不管是哪一個,$mod\space lcm$即mod p都是x。我們也就求出了答案)
$\frac{n!}{m!(n-m)!} = a_i\space mod \space pk$
現在不互質的就是pi的倍數
首先我們可以把分子分母的所有$pi$質因子都提出來,然後上下次數相消。
對於$n!$中p的質因個數,就是不斷除以p^i下取整。
剩下的都是和pk互質的了。存在逆元
以求$19!\space mod\space 3^2$為例
$19!=1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19$
提完質數3之後,變成:
$=(1*2*3*4*5*6)*3^2*(1*2*4*5*7*8*10*11*13*14*16*17*19)$
前面直接遞歸下去處理。後面的每一項,都可以%pk處理。
(不論分母還是分子位置,如果是分母位置,因為存在逆元,
10*inv =1 mod 9
1*inv = 1 mod 9
這兩個inv顯然是同一個inv.所以把所有項%pk沒有問題
)
然後變成:
$=(1*2*3*4*5*6)*3^2*(1*2*4*5*7*8)^2*1)$
後面那個1是多出來的19
其實pk長度的循環節有n/pk個。直接算。剩下(例如這裏的19),個數少於pk,直接算。
(所以,擴展LUCAS的重要適用條件是,$p_i^{q_i}$不能太大(1e5左右))
遞歸算出來即可。
對於分母位置的兩個階乘,算出來結果之後,再處理inv
(這裏可以先乘完之後再找inv,不用一邊找inv一邊乘。)
(註意inv處理要用exgcd,不保證質數,不能用費馬)
(CRT可以不用保存結果,Mi=p/pk 可以一次到位)
#include<bits/stdc++.h> #define reg register int #define il inline #define int long long #define numb (ch^‘0‘) using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ ll p; ll qm(ll x,ll y,ll pk){ x%=pk; ll ret=1; while(y){ if(y&1) ret=(ret*x)%pk; x=(x*x)%pk; y>>=1; } return ret; } ll calc(ll n,ll pi,ll pk){//計算階乘部分 (質因子已經提前處理這裏不予考慮) if(!n) return 1; ll res=1; for(reg i=2;i<pk;++i)//每個循環節 if(i%pi) res=(res*i)%pk; res=qm(res,n/pk,pk); for(reg i=2;i<=n%pk;++i) if(i%pi) res=(res*i)%pk; return res*calc(n/pi,pi,pk)%pk; } void exgcd(ll a,ll b,ll &x,ll &y){//exgcd if(!b){ x=1,y=0;return; } exgcd(b,a%b,y,x); y-=(a/b)*x; } ll inv(ll n,ll pk){//逆元 ll x,y;exgcd(n,pk,x,y); x=(x%pk+pk)%pk; return x; } ll C(ll n,ll m,ll pi,ll pk){//計算C(n,m)mod pi^k ll up=calc(n,pi,pk),d1=calc(m,pi,pk),d2=calc(n-m,pi,pk); ll k=0; for(reg i=n;i;i/=pi) k+=i/pi;//處理質因子個數 for(reg i=m;i;i/=pi) k-=i/pi; for(reg i=n-m;i;i/=pi) k-=i/pi; return up*inv(d1,pk)%pk*inv(d2,pk)%pk*qm(pi,k,pk)%pk; } ll CRT(ll b,ll mod){//CRT每步算出來了之後直接合並 return (b*inv(p/mod,mod)%p*(p/mod))%p; } ll EXLUCAS(ll n,ll m){//質因數分解+開始處理C ll ret=0; ll tmp=p; for(reg i=2;(ll)i*i<=tmp;++i){ if(tmp%i==0){ ll pi=i,pk=1; while(tmp%i==0) pk*=i,tmp/=i; (ret+=CRT(C(n,m,pi,pk),pk))%=p; } } if(tmp>1) (ret+=CRT(C(n,m,tmp,tmp),tmp))%=p; return ret; } int main(){ ll n,m; scanf("%lld%lld%lld",&n,&m,&p); printf("%lld",EXLUCAS(n,m)); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/12/1 8:44:42 */
這個算法的核心思路是:
1.不互質的要提出來單獨處理
2.直接處理P,不互質的太多了
3.分成質因子處理,CRT合並
4.對於階乘上下提出不互質的部分(質因子),轉化成互質存在逆元的情況
5.觀察剩余部分,後面可以對pk取模。
發現一部分還是階乘,遞歸處理。
另一部分發現有循環節,利用循環節加速處理。
剩下的邊角考慮一下。
(5本質上就是對每個數提取pi質因子,剩下的再乘起來。不過用遞歸和循環節加速了一下)
還有一個無聊的題:
[國家集訓隊]禮物
簡單的組合數學,非要考你擴展LUCAS。。。。
[學習筆記]擴展LUCAS定理