数论基础模板
壹——最大公约数
欧几里得算法:
gcd(x,y)=gcd(y,x%y);
边界条件:if(y==0)return x;
#include<bits/stdc++.h> using namespace std; int a,b; inline int gcd(int x,int y) { if(!y)return x; else return gcd(y,x%y); } int main() { cin>>a>>b; cout<<gcd(a,b)<<endl; return 0; }
贰——欧拉函数
定义法:
#include<bits/stdc++.h> using namespace std; int ol(int x) { int y=x; for(int j=2;j<=x/j;j++) if(x%j==0) { y=y/j*(j-1); while(x%j==0)x/=j; } if(x>1)y=y/x*(x-1); return y; } int main() { int x; scanf("%d",&x); printf("%d\n",ol(x)); return 0; }
筛法:
#include<bits/stdc++.h> #define N 1000010 #define ll long long using namespace std; ll phi[N],pr[N]; bool st[N]; int n,k; void ol(int x) { phi[1]=1; for(int i=2;i<=x;i++) { if(st[i]==0) { phi[i]=i-1; pr[k++]=i; } for(int j=0;pr[j]<=x/i;j++) { st[pr[j]*i]=1; if(i%pr[j]==0) { phi[i*pr[j]]=pr[j]*phi[i]; break; } phi[i*pr[j]]=(pr[j]-1)*phi[i]; } } ll ans=0; for(int i=1;i<=x;i++)ans+=phi[i]; printf("%lld",ans); } int main() { scanf("%d",&n); ol(n); return 0; }
叁——快速幂
#include<bits/stdc++.h> #define ll long long using namespace std; ll ksm(ll x,ll y,ll MOD) { ll sum=1; while(y) { if(y%2) { sum=sum*x%MOD; } x=x*x%MOD; y/=2; } return sum; } int main() { ll a,b,mod; int k; cin>>k; cin>>a>>b>>mod; cout<<ksm(a,b,mod)<<endl; return 0; }
肆——逆元
当n为质数时,可以用快速幂求逆元:
a/b ≡ a * x (mod n)
两边同乘b可得 a ≡ a * b * x (mod n)
即 b * x ≡ 1 (mod n)
由费马小定理可知,当n为质数时
b ^ (n - 1) ≡ 1 (mod n)
b * b ^ (n - 2) ≡ 1 (mod n)
#include <iostream> using namespace std; typedef long long LL; LL qmi(int a, int b, int p) { LL res = 1; while(b){ if(b & 1) res = res * a % p; a = (LL)a * a % p; b >>= 1; } return res; } int main() {int a, p; cin >> a >> p; if(a % p == 0) puts("impossible"); else cout << qmi(a, p - 2, p) << endl;
return 0; }
当n不是质数时,可以用扩展欧几里得算法求逆元:
a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1
假设a的逆元为x,那么有a * x ≡ 1 (mod p)
等价:ax + py = 1
#include <iostream> using namespace std; typedef long long LL; int n; int exgcd(int a, int b, int &x, int &y) { if (!b) { x = 1, y = 0; return a; } int d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } int main() { cin >> n;int a, p, x, y; cin >> a >> p; int d = exgcd(a, p, x, y); if (d == 1) cout << ((LL)x + p) % p << endl; else puts("impossible"); return 0; }
线性逆元
#include<bits/stdc++.h> #define N 1000000 #define MOD 9901 using namespace std; int ny[N]; int main() { int ny[1]=1; int n; cin>>n; for(int i=2;i<=n;i++) ny[i]=-MOD/i*ny[p%i]%MOD; return 0; }
伍—— 扩展欧几里得
用于求解方程 ax+by=gcd(a,b)的解
当 b=0时:ax+by=a 故而 x=1,y=0
当 b≠0时:因为gcd(a,b)=gcd(b,a%b)
而 bx′+(a%b)y′=gcd(b,a%b)
bx′+(a−⌊a/b⌋∗b)y′=gcd(b,a%b)
ay′+b(x′−⌊a/b⌋∗y′)=gcd(b,a%b)=gcd(a,b)
故而x=y′,y=x′−⌊a/b⌋∗y′
因此可以采取递归算法 先求出下一层的x′和y′再利用上述公式回代即可
#include<bits/stdc++.h> using namespace std; int n; int x, y; inline int exgcd(int a,int b) { if(b==0) { x=1,y=0; return a; } else { int gcd=exgcd(b,a%b); int z=x; x=y; y=z-a/b*y; return gcd; } } int main() { int a,b; scanf("%d%d",&a,&b); exgcd(a,b); printf("%d %d\n",x,y); return 0; }
求解一般一般的方程 ax+by=c
设 d=gcd(a,b)
则其有解当且仅当 d|c
求解方法如下:用扩展欧几里得求出 ax0+by0=d的解
则a(x0∗c/d)+b(y0∗c/d)=c
故而特解为 x'=x0∗c/d,y'=y0∗c/d
而 通解=特解+齐次解
而 齐次解即为方程 ax+by=0的解
故而通解为x=x'+k∗b/d,y=y'−k∗a/d(k∈Z)
一次同余方程:ax≡b(modm)
ax=m∗(−y)+b
ax+my=b
#include<bits/stdc++.h> #define ll long long using namespace std; int n; ll x,y; inline ll exgcd(ll a,ll b) { if(b==0) { x=1,y=0; return a; } else { int gcd=exgcd(b,a%b); int z=x; x=y,y=z-a/b*y; return gcd; } } int main() { cin>>n; ll a,b,m; scanf("%lld %lld %lld",&a,&b,&m); int d=exgcd(a,m); if(b%d==0) { ll t=b/d; printf("%lld\n",(x*t+m)%m); } else puts("impossible"); } return 0; }
陆——线行筛法
#include<bits/stdc++.h> #define N 1000000 using namespace std; int pir[N],vis[N],cut; void xian() { int n=N; for(int i=2;i<=n;i++) { if(pir[i]==0)vis[++cut]=i,pir[i]=i; for(int j=1;j<=cut;j++) { if(vis[j]>pir[i]||vis[j]>n/i)break; pir[i*vis[j]]=vis[j]; } } } int main() { xian(); return 0; }