ACM基础数论笔记
基础数论部分
整除
定义
设
不能整除
定理
约数
对于一个数n,其约数定义为能够整除n的数,在整数域内等价于因数
试除法求n的所有约数:
vector<int> get_divisors(int x){
vector<int> res;
for (int i = 1; i <= x / i; i ++ )
if (x % i == 0){
res.push_back(i);
if (i != x / i) res.push_back(x / i);
}
sort(res.begin(), res.end());
return res;
}
最大公约数(GCD)
指两个或多个整数中共有的约数中最大的一个
特别的
最常见使用辗转相除法求gcd
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
最小公倍数(LCM)
两个或多个整数最小的公共倍数,
公式:
ll lcm(ll m,ll n){
ll g1,b;
g1 = __gcd(m,n);
b = (m*n) / g1;
return b;
}
质数
一个正数p除了
若a是一个合数,则必有质数p,
质数的个数是无穷的
判定质数的方法
试除法判质数
适用于数据范围
试除法的原理就是用
时间复杂度
bool isPrime(int x){
if(x<2){
return false;
}
for(int i=2;i<=x/i;i++){
if(x%i==0){
return false;
}
}
return true;
}
//常数优化 sqrt(x)/3
bool isPrime(int n) {
if (n == 1) return false;
if (n == 2 || n == 3) return true;
if (n % 6 != 1 && n % 6 != 5) return false;
for (int i = 5, j = n / i; i <= j; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
Miller Rabin素性测试
适用于数据范围
费马素性测试:基于费马小定理
找一个数x,判定
正是由于费马小定理无法判断伪质数也叫Carmichael数,所以费马素性测试并不能保证完全正确
二次探测定理:如果
Miller Rabin算法在费马素性测试基础上通过二次探测定理改进而来
如果一个数满足方程
ll mul(ll a, ll b, ll m) {
return static_cast<__int128_t>(a) * b % m;
}
ll power(ll a, ll b, ll m) {
ll res = 1 % m;
for (; b; b >>= 1, a = mul(a, a, m))
if (b & 1)
res = mul(res, a, m);
return res;
}
bool isprime(ll n) {
if (n < 2)
return false;
static constexpr int A[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int s = __builtin_ctzll(n - 1);
ll d = (n - 1) >> s;
for (auto a : A) {
if (a == n)
return true;
ll x = power(a, d, n);
if (x == 1 || x == n - 1)
continue;
bool ok = false;
for (int i = 0; i < s - 1; ++i) {
x = mul(x, x, n);
if (x == n - 1) {
ok = true;
break;
}
}
if (!ok)
return false;
}
return true;
}
标准分解形式
算数基本定理:任意一个大于1的整数都可以被分解成若干个质数相乘的形式
-
设
若
,则m是n的正因数 -
设
,则n的正因数个数有 个 -
设
则
其中
-
约数之和:
质因数分解
试除法分解质因数
时间复杂度
map<int,int> cnt;
void divide(int x){
for(int i=2;i<=x/i;i++){
if(x%i==0){
while(x%i==0){
x/=i;
cnt[i]++;
}
}
}
if(x>1) cnt[x]++;
}
Pollard Rho算法
pollard rho用于求解大数质因子问题,使用Miller Rabin判断质数,原理是生日悖论,了解即可
ll mul(ll a, ll b, ll m) {
return static_cast<__int128_t>(a) * b % m;
}
ll power(ll a, ll b, ll m) {
ll res = 1 % m;
for (; b; b >>= 1, a = mul(a, a, m))
if (b & 1)
res = mul(res, a, m);
return res;
}
bool isprime(ll n) {//Miller Rabin部分
if (n < 2)
return false;
static constexpr int A[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int s = __builtin_ctzll(n - 1);
ll d = (n - 1) >> s;
for (auto a : A) {
if (a == n)
return true;
ll x = power(a, d, n);
if (x == 1 || x == n - 1)
continue;
bool ok = false;
for (int i = 0; i < s - 1; ++i) {
x = mul(x, x, n);
if (x == n - 1) {
ok = true;
break;
}
}
if (!ok)
return false;
}
return true;
}
std::vector<ll> factorize(ll n) {//pollard rho部分,所有的质因数都会作为vector元素返回
std::vector<ll> p;
std::function<void(ll)> f = [&](ll n) {
if (n <= 10000) {
for (int i = 2; i * i <= n; ++i)
for (; n % i == 0; n /= i)
p.push_back(i);
if (n > 1)
p.push_back(n);
return;
}
if (isprime(n)) {
p.push_back(n);
return;
}
auto g = [&](ll x) {
return (mul(x, x, n) + 1) % n;
};
ll x0 = 2;
while (true) {
ll x = x0;
ll y = x0;
ll d = 1;
ll power = 1, lam = 0;
ll v = 1;
while (d == 1) {
y = g(y);
++lam;
v = mul(v, std::abs(x - y), n);
if (lam % 127 == 0) {
d = std::__gcd(v, n);
v = 1;
}
if (power == lam) {
x = y;
power *= 2;
lam = 0;
d = std::__gcd(v, n);
v = 1;
}
}
if (d != n) {
f(d);
f(n / d);
return;
}
++x0;
}
};
f(n);
std::sort(p.begin(), p.end());
return p;
}
整除函数
定义
设
性质
-
-
-
设
-
-
带余除法:设
-
若
-
二元一次不定方程
定义
未知数必须受到某种限制(如整数,正整数,有理数等)的方程,称为不定方程
设
定理
-
设二元一次不定方程
有一整数解
则此不定方程的一切整数解可以表示为 -
二元一次不定方程
有整数解的充要条件是 -
裴蜀定理:二元一次不定方程
一定存在x,y 使得可以拓展到多元一次不定方程:
void solve(){ int n; cin>>n; vector<int> a(n); in(a,n);//输入n个系数 int sum=0; For(i,0,a.size()){ sum=__gcd(sum,abs(a[i]));//gcd(0,a)=a } cout<<sum<<"\n"; }
扩展欧几里得(exGCD)
求解二元一次不定方程
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int g = exgcd(b, a % b, y, x);
y -= a / b * x;
return g;
}
如何用扩展欧几里得算法求二元一次不定方程
先用exgcd求出
通解便是
使用扩展欧几里得求线性同余方程:设同余方程
根据同余定义,我们可以将同余方程化为二元一次不定方程
eg:[P1516 青蛙的约会 - 洛谷](P1516 青蛙的约会 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int g = exgcd(b, a % b, y, x);
y -= a / b * x;
return g;
}
void solve(){
int a,b,m,n,l;
cin>>a>>b>>m>>n>>l;
if(n-m<0)swap(a,b),swap(m,n);
int x,y;
int g=exgcd((n-m),l,x,y);
if((a-b)%g){//如果最大公约数不是(a-b)的倍数一定无解
cout<<"Impossible\n";
}else{
cout<<((x*((a-b)/g))%(l/g)+(l/g))%(l/g)<<"\n";//最小正整数解
}
}
同余
定义
设
性质
-
同余具有自反,对称,传递性,同余是等价关系
-
若
-
若
,反之不然 -
若
-
若
-
若
-
若
完全剩余系
如果a,b关于m同余,则a与b属于同一类,否则不属于同一类
这样可以得到m个类,即
从每个剩余数中各取一个数作为代表,这样得到的m个数成为模m的一个完全剩余系,也叫完系,比如
当m为奇数时,
当m为偶数时,
当
如果
缩系/既约剩余系
在完全剩余系中,与m互质的元素组成的子集
中国剩余定理(CRT)
求解同余方程的一个解,要求
x的解公式:
其中,
excrt板子:
using ll = long long;
const int N = 1e5+10;
ll Ai[N], Mi[N];
int n;
constexpr int qpow(int n, int k, int p) {
int r = 1;
for (; k; k >>= 1, n = n * n % p)
if (k & 1) r = r * n % p;
return r;
}
constexpr ll mul(ll a, ll b, ll p) {
ll res = a * b - ll(1.L * a * b / p) * p;
res %= p;
if (res < 0) {
res += p;
}
return res;
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) { x = 1, y = 0; return a; }
ll gcd = exgcd(b, a % b, x, y), tp = x;
x = y, y = tp - a / b * y;
return gcd;
}
ll excrt() {
ll x, y, k;
ll M = Mi[1], ans = Ai[1];
for (int i = 2; i <= n; ++ i) {
ll a = M, b = Mi[i], c = (Ai[i] - ans % b + b) % b;
ll gcd = exgcd(a, b, x, y), bg = b / gcd;
if (c % gcd != 0) return -1;
x = mul(x, c / gcd, bg);
ans += x * M;
M *= bg;
ans = (ans % M + M) % M;
}
return (ans % M + M) % M;
}
费马小定理
定义
多项式展开中
其中
而
从而有
令x=1,y=1,可得
因此对于所有
得到费马小定理:对于任意整数a和质数p,都有
若
其逆定理不成立,即使得
341最小伪质数,1000以下还有561和645是伪质数
威尔逊定理
若p为质数,则
逆元
所以从费马小定理可以得出,
逆元可以在模意义下用乘法代替原数除法进行运算,即
费马小定理求逆元代码
const int mod = 1e9+7;
int qpow(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int inv(int x){return qpow(x,mod-2);}
但是费马小定理求逆元也有局限性,就是p必须为一个质数,除了费马小定理,我们也可以使用扩展欧几里得求逆元,它要求a与模数p互质即可,p不必须为质数
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int g = exgcd(b, a % b, y, x);
y -= a / b * x;
return g;
}
int inv(int a, int p){
int x, y;
exgcd(a, p, x, y);
return (p + x % p) % p;
}
欧拉定理
费马定理阐述的是在质数模下,指数的同余性质,当模是合数时,就要用欧拉定理
欧拉函数
互质:
求单个数字的欧拉函数:
int phi(int x){
int res = x;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0){
res = res / i * (i - 1);
while (x % i == 0) x /= i;
}
if (x > 1) res = res / x * (x - 1);
return res;
}
引理
-
若
为一个质数,则 -
若
为某一个素数p的幂次 ,则 -
若
为两个质数 的积,则 -
设
,则
欧拉定理
设n为正整数,考虑
也就是说,若a与n互质,则
扩展欧拉定理
此时x与m可以不互质
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析