一.数论 (讲解)
求单一欧拉函数 时间:O ( $sqrt{n}$ )
int eular(int a) { int ans=1; for(int i=2;i*i<=a;i++) { if(!(a%i)) { a/=i; ans*=i-1; for(;!(a%i);a/=i) ans*=i; } } if(a>1) ans*=a-1; return ans; }
欧拉/莫比乌斯/质数线性筛 时间:O ( $n$ )
int pri[MN+5],cnt,miu[MN+5],phi[MN+5]; bool ok[MN+5]; void init() { phi[1]=1; for(int i=2;i<=MN;i++) { if(!ok[i]){pri[++cnt]=i;phi[i]=i-1;miu[i]=-1;} for(int j=1;j<=cnt&&pri[j]*i<=MN;j++) { ok[pri[j]*i]=1; if(i%pri[j]) { phi[pri[j]*i]=phi[i]*(pri[j]-1); miu[pri[j]*i]=-miu[i]; } else { phi[pri[j]*i]=phi[i]*pri[j]; miu[pri[j]*i]=0; break; } } } }
逆元线性筛(条件:模数为质数)时间:O ( $n$ )
typedef long long ll; int inv[MN+5]; void init() { inv[1]=1; for(int i=2;i<=MN;i++) inv[i]=(ll)(Mod-Mod/i)*inv[Mod%i]%Mod; }
费马小定理求逆元(条件:模数为质数) 时间:O ( $log_2Mod$ )
typedef long long ll; int quipow(int a,int b,int Mod) //快速幂 { int ret=1; for(;b;b>>=1,a=(ll)a*a%Mod) if(b&1) ret=(ll)ret*a%Mod; return ret; } int getinverse(int x,int Mod) { //费马小定理 return quipow(x,Mod-2,Mod); }
gcd(欧几里得算法) 时间:O ( $log_2a$ )
int Gcd(int a,int b){return b==0?a:Gcd(b,a%b);}
exgcd(扩展欧几里得算法) 时间:O ( $log_2a$ )
void exgcd(int a,int b,int&x,int&y) { if(!b) x=1,y=0; else exgcd(b,a%b,y,x),y-=a/b*x; }
exgcd求逆元 (条件:$gcd(a,Mod)$ $=$ $1$ ) 时间:O ( $log_2a$ )
int getinverse(int x,int Mod) { int ans,y; exgcd(x,Mod,ans,y); return (ans%Mod+Mod)%Mod; }
欧拉函数求逆元(条件:$gcd(a,Mod)$ $=$ $1$ ) 时间:O ( $sqrt{a}$ )
int quipow(int a,int b,int Mod) //快速幂 { int ret=1; for(;b;b>>=1,a=(ll)a*a%Mod) if(b&1) ret=(ll)ret*a%Mod; return ret; } int eular(int a) //求欧拉函数 { int ans=1; for(int i=2;i*i<=a;i++) { if(!a%i) { a/=i; ans*=i-1; for(;a%i;a/=i) ans*=i; } } if(a>1) ans*=a-1; return ans; } int getinverse(int a,int Mod) { //欧拉函数求逆元 return quipow(a,eular(Mod)-1,Mod); }
bsgs(大步小步)(拔山盖世) (条件:$gcd(a,p)$ $=$ $1$) 时间:O( 哈希复杂度 $ imes$ $sqrt{Mod}$ )
int bsgs(int a,int b,int p) //a^x=b mod p { std::map<int, int> h; if(b==1) return 0; int m=ceil(sqrt(p)),inv=getinverse(a,p),now=quipow(a,m-1,p),s; for(int i=m-1;i>=0;i--,now=(ll)now*inv%p) { //将b*a^(0~m-1)丢进hash表 s=(ll)now*b%p; if(h.find(s)==h.end()) h.insert(P(s,i)); } inv=quipow(a,m,p);now=1; for(int i=1;i<=m;i++) //在hash表里找与a^(i*m)相等的值 { now=(ll)now*inv%p; if(h.find(now)!=h.end()) return i*m-h[now]; } return -1; }
exbsgs(扩展大步小步) 时间:O ( $log_2a$ $+$ bsgs复杂度 )
int exbsgs(int a,int b,int p) //a^x=b mod p { int k=0; for(int d=Gcd(a,p);d>1;d=Gcd(a,p)) //找到最小的k使得gcd(a,p/gcd(a^k,p))=1 { if(b%d) return -1; b/=d;p/=d; b=(ll)b*getinverse(a/d,p)%p; k++; } int ans=bsgs(a,b,p); if(ans==-1) return -1; else return ans+k; }
CRT(中国剩余定理/孙子定理)(条件:对于$forall i,j , i eq j$,$gcd(m_{i},m_{j}) = 1$) 时间:O ( $n$ $cdot$ $log_2m_{i}$ )
int CRT(int n,int *a,int *m) //X = a[i] mod m[i] { int M=1,ans=0,w; for(int i=1;i<=n;i++) M*=m[i]; for(int i=1;i<=n;i++) { w=M/m[i]; ans=(ans+(ll)a[i]*w%M*getinverse(w,m[i])%M)%M; } return (ans%M+M)%M; }
Lucas定理 (条件:$pin mathbb{P}$) 时间:O ( $log_pn$ $cdot$ $log_2p$ $+$ $p$ )
int C(int n,int m,int Mod) { if(m>n) return 0; return (ll)fac[n]*getinverse(fac[m],Mod)%Mod*getinverse(fac[n-m],Mod)%Mod; } int lucas(int n,int m,int p) //C(n,m) % p { if(!m) return 1; return (ll)C(n%p,m%p,p)*lucas(n/p,m/p,p)%p; }
exlucas(扩展lucas) 时间:设P质因数分解后为$ = p_{1}^{k_{1}} cdot p_{2}^{k_{2}} cdot .... cdot p_{t}^{k_{t}}$,则复杂度为O ( CRT复杂度+$sum_{i=1}^{t} p_{i}^{k_{i}} cdot log_{p_{i}}n$ )
int calc(int n,int p,int pt) { if(!n) return 1; int ans=1; for(int i=2;i<=pt;i++) if(i%p) ans=(ll)ans*i%pt; ans=quipow(ans,n/pt,pt); for(int i=2;i<=n%pt;i++) if(i%p) ans=(ll)ans*i%pt; return (ll)ans*calc(n/p,p,pt)%pt; } int mutilucas(int n,int m,int p,int pt) //C(n,m) mod p^t { int cnt=0; for(int i=n;i;i/=p) cnt+=i/p; for(int i=m;i;i/=p) cnt-=i/p; for(int i=n-m;i;i/=p) cnt-=i/p; return (ll)quipow(p,cnt,pt)*calc(n,p,pt)%pt*getinverse(calc(m,p,pt),pt)%pt*getinverse(calc(n-m,p,pt),pt)%pt; } int exlucas(int n,int m,int P) //C(n,m) mod P { if(m>n) return 0; int cnt=0,p[40],a[40]; for(int i=2;i*i<=P;i++) { if(P%i==0) { p[++cnt]=1; for(;P%i==0;) { P/=i; p[cnt]*=i;; } a[cnt]=mutilucas(n,m,i,p[cnt]); } } if(P>1) p[++cnt]=P,a[cnt]=mutilucas(n,m,P,P); return CRT(cnt,a,p); }