算法笔记--数论模板小集(待增)
gcd
一行版:
unsigned int gcd(unsigned int a,unsigned int b) { return b>0?gcd(b,a%b):a; }
快速幂和快速乘
ll pow(ll n,ll k) { ll ans=1; while(k) { if(k%2) ans*=n; n*=n; k/=2; } return ans; } ll mul(ll n,ll k) { int ans=0; while(k) { if(k%2) ans+=n; n+=n;//n*=2; k/=2; } return ans; }
扩展欧几里得(扩展gcd)
int e_gcd(int a,int b,int &x,int &y) { if(!b) { x=1; y=0; return a; } int ans=e_gcd(b,a%b,x,y); int temp=x; x=y; y=temp-a/b*y; return ans; } int e_gcd(int a,int b,int &x,int &y){ if (!b) { x=1,y=0; return a; } int ans=e_gcd(b,a%b,y,x);//注意x,y位置和上面不同 y-=a/b*x; return ans; }
乘法逆元
扩展欧几里得版:
ll e_gcd(ll a,ll b,ll &x,ll &y) { if(!b) { x=1; y=0; return a; } ll ans=e_gcd(b,a%b,x,y); ll temp=x; x=y; y=temp-a/b*y; return ans; } ll inv(ll a,ll n) { ll x,y,d=e_gcd(a,n,x,y); if(d==1)return (x%n+n)%n; else return -1; }
费马小定理版:
当模数不为质数时,是 a ^(phi(p) - 1)
LL pow_mod(LL a, LL b, LL p){//a的b次方求余p LL ret = 1; while(b){ if(b & 1) ret = (ret * a) % p; a = (a * a) % p; b >>= 1; } return ret; } LL Fermat(LL a, LL p){//费马求a关于p的逆元 return pow_mod(a, p-2, p); }
逆元递推版
const int N = 200000 + 5; const int MOD = (int)1e9 + 7; int inv[N]; int init(){ inv[1] = 1; for(int i = 2; i < N; i ++){ inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD; } }
普通素数筛(埃筛,复杂度O(nlog(log(n))))
void prime(int n) { for(int i=2;i<=n;i++) { if(!not_prime[i]) { for(int j=i+i;j<=n;j+=i) not_prime[j]=true; } } }
线性素数筛(欧拉筛,复杂度O(n))
const int N=1e9+5; const int M=1e7+5; bool not_prime[N]={false}; int prime[M]; int tot; void euler_sieve(int n) { tot=0; for(int i=2;i<=n;i++) { if(!not_prime[i])prime[tot++]=i; for(int j=0;i*prime[j]<=n;j++) { not_prime[i*prime[j]]=true; if(i%prime[j]==0)break; //用最小的质因数筛,应为i%prime[j]==0,所以i=prime[j]*k,那么下一次循环要筛的是i*prime[j+1]=k*prime[j]*prime[j+1],没有必要筛,直接退出 } } }
求单个欧拉函数
ll Euler(ll n) { ll ans=n; for(ll i=2;i*i<=n;i++) { if(n%i==0)ans=ans/i*(i-1); while(n%i==0)n/=i; } if(n>1)ans=ans/n*(n-1); return ans; }
普通筛法求欧拉函数(复杂度O(nlog(log(n))))
void Euler(){ phi[1] = 1; for(int i = 2; i < N; i ++){ if(!phi[i]){ for(int j = i; j < N; j += i){ if(!phi[j]) phi[j] = j; phi[j] = phi[j] / i * (i-1); } } } }
线性筛法求欧拉函数(复杂度O(n))
void Euler(int n) { phi[1]=1; int k=0; for(int i=2;i<=n;i++) { if(!not_prime[i]) { phi[i]=i-1; prime[k++]=i; } for(int j=0;i*prime[j]<=n;j++) { not_prime[i*prime[j]]=true; if(i%prime[j]==0)//根据欧拉函数的性质。 { phi[i*prime[j]]=phi[i]*prime[j]; break; } else phi[i*prime[j]]=phi[i]*phi[prime[j]]; } } }
线性筛求莫比乌斯反演函数(复杂度O(n))
void Mobius() { mem(vis,false); mu[1]=1; cnt=0; for(int i=2;i<N;i++) { if(!vis[i]) { prime[cnt++]=i; mu[i]=-1; } for(int j=0;j<cnt&&i*prime[j]<N;j++) { vis[i*prime[j]]=true; if(i%prime[j])mu[i*prime[j]]=-mu[i]; else { mu[i*prime[j]]=0; break; } } } }
中国剩余定理:
int ex_gcd(int a,int b,int &x,int &y) { if(b==0) { x=1; y=0; return a; } int ans=ex_gcd(b,a%b,y,x); y-=a/b*x; return ans; } int CRT(int a[],int m[],int n) { int M=1; int ans=0; for(int i=1;i<=n;i++)M*=m[i]; for(int i=1;i<=n;i++) { int x,y; int Mi=M/m[i]; ex_gcd(Mi,m[i],x,y); ans=(ans+a[i]*Mi*x)%M; } if(ans<0)ans+=M; return ans; }
米勒罗宾素数测试
ll mod_qmul(ll n,ll k,ll mod) { ll ans=0; while(k) { if(k&1)ans=(ans+n)%mod; n=(n+n)%mod; k>>=1; } return ans; } ll mod_qpow(ll n,ll k,ll mod) { ll ans=1; while(k) { if(k&1)ans=mod_qmul(ans,n,mod); n=mod_qmul(n,n,mod); k>>=1; } return ans; } bool check(ll a,ll n,ll x,ll t) { ll ret=mod_qpow(a,x,n); ll last=ret; for(int i=0;i<t;i++) { ret=mod_qmul(ret,ret,n); if(ret==1&&last!=1&&last!=n-1)return true; last=ret; } if(ret!=1)return true; return false; } bool Miller_Rabin(ll n,int s) { if(n==2)return true; if(n<2||(n&1)==0)return false; ll x=n-1,t=0; while((x&1)==0)x>>=1,t++; for(int i=0;i<s;i++) { ll a=rand()%(n-1)+1; if(check(a,n,x,t))return false; } return true; }
卢卡斯定理求组合数
void init() { fac[0]=1; for(int i=1;i<=p;i++)fac[i]=fac[i-1]*i%p; } ll q_pow(ll n,ll k) { ll ans=1; while(k) { if(k&1)ans=ans*n%p; n=n*n%p; k>>=1; } return ans; } /*当p很大时*/ ll C(ll n,ll m) { if(n<m)return 0; if(n-m<m)m=n-m; ll s1=1,s2=1; for(int i=0;i<m;i++) { s1=s1*(n-i)%p; s2=s2*(i+1)%p; } return s1*q_pow(s2,p-2)%p; } /*当p很小时 */ ll C(ll n,ll m) { if(m>n)return 0; return fac[n]*q_pow(fac[m]*fac[n-m]%p,p-2)%p; } ll lucas(ll n,ll m) { if(m==0)return 1; return (C(n%p,m%p)*lucas(n/p,m/p))%p; }
未完待续。。。