[模板] gcd、exgcd、乘法逆元、快速冪、快速乘、篩素數、快速求逆元、組合數
阿新 • • 發佈:2019-02-06
1.gcd int gcd(int a,int b){ return b?gcd(b,a%b):a; } 2.擴充套件gcd )extend great common divisor ll exgcd(ll l,ll r,ll &x,ll &y) { if(r==0){x=1;y=0;return l;} else { ll d=exgcd(r,l%r,y,x); y-=l/r*x; return d; } } 3.求a關於m的乘法逆元 ll mod_inverse(ll a,ll m){ ll x,y; if(exgcd(a,m,x,y)==1)//ax+my=1 return (x%m+m)%m; return -1;//不存在 } 補充:求逆元還可以用 ans=abmodm=(amod(m⋅b))/b 4.快速冪quick power ll qpow(ll a,ll b,ll m){ ll ans=1; ll k=a; while(b){ if(b&1)ans=ans*k%m; k=k*k%m; b>>=1; } return ans; } 5.快速乘,直接乘會爆ll時需要它,也叫二分乘法。 ll qmul(ll a,ll b,ll m){ ll ans=0; ll k=a; ll f=1;//f是用來存負號的 if(k<0){f=-1;k=-k;} if(b<0){f*=-1;b=-b;} while(b){ if(b&1) ans=(ans+k)%m; k=(k+k)%m; b>>=1; } return ans*f; } 6.中國剩餘定理CRT (x=ai mod mi) ll china(ll n, ll *a,ll *m) { ll M=1,y,x=0,d; for(ll i = 1; i <= n; i++) M *= m[i]; for(ll i = 1; i <= n; i++) { ll w = M /m[i]; exgcd(m[i], w, d, y);//m[i]*d+w*y=1 x = (x + y*w*a[i]) % M; } return (x+M)%M; } 7.篩素數,全域性:int cnt,prime[N],p[N]; void isprime() { cnt = 0; memset(prime,true,sizeof(prime)); for(int i=2; i<N; i++) { if(prime[i]) { p[cnt++] = i; for(int j=i+i; j<N; j+=i) prime[j] = false; } } } 8.快速計算逆元 補充:>>關於快速算逆元的遞推式的證明<< void inverse(){ inv[1] = 1; for(int i=2;i<N;i++) { if(i >= M) break; inv[i] = (M-M/i)*inv[M%i]%M; } } 9.組合數取模 n和m 10^5時,預處理出逆元和階乘 ll fac[N]={1,1},inv[N]={1,1},f[N]={1,1}; ll C(ll a,ll b){ if(b>a)return 0; return fac[a]*inv[b]%M*inv[a-b]%M; } void init(){//快速計算階乘的逆元 for(int i=2;i<N;i++){ fac[i]=fac[i-1]*i%M; f[i]=(M-M/i)*f[M%i]%M; inv[i]=inv[i-1]*f[i]%M; } } n較大10^9,但是m較小10^5時, ll C(ll n,ll m){ if(m>n)return 0; ll ans=1; for(int i=1;i<=m;i++) ans=ans*(n-i+1)%M*qpow(i,M-2,M)%M; return ans; } n和m特別大10^18時但是p較小10^5時用lucas 10.Lucas大組合取模 #define N 100005 #define M 100007 ll n,m,fac[N]={1}; ll C(ll n,ll m){ if(m>n)return 0; return fac[n]*qpow(fac[m],M-2,M)%M*qpow(fac[n-m],M-2,M)%M;//費馬小定理求逆元 } ll lucas(ll n,ll m){ if(!m)return 1; return(C(n%M,m%M)*lucas(n/M,m/M))%M; } void init(){ for(int i=1;i<=M;i++) fac[i]=fac[i-1]*i%M; }