嘘~ 正在从服务器偷取页面 . . .

OI数学总结-数论


OI数学总结-数论

注意,本文不同于 OI模板-数学

本文倾向于数学概念,但是包含部分代码,只是因为数论的内容与代码联系紧密,因此显得较为相似。


积性函数

数论中定义:定义域为正整数域的函数 $f(n)$ ,

且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b) \land \gcd(a,b)=1$


完全积性函数

数论中定义:定义域为正整数域的函数 $f(n)$ ,

且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b)$


最大公约数 gcd

最大公约数指能够整除多个不全为零的整数的最大正整数

常用性质: $a\times b = \gcd(a,b) \times \mathrm{lcm}(a,b)$

代码求解

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
} // 注意这份代码对于(+,-)的情况会求出负数

裴蜀定理

设 $a,b$ 是不全为零的整数,则存在整数,使得 $ax+by=\gcd(a,b)$

证明详见 裴蜀定理及其证明


扩展欧几里德算法(Exgcd)

板子题: P5656 【模板】二元一次不定方程 (exgcd)

若 $b \ne 0$ ,扩展欧几里得算法求出的可行解必有 $|x| \le b ,|y| \le a$ 。

int exgcd(int a,int b,int &x,int &y)
{
    int d=a;
    if(!b)x=1,y=0;
    else d=exgcd(b,a%b,y,x),y-=a/b*x;
    return d;
}
void solve()
{
    int a,b,c,x,y;
    read(a);read(b);read(c);
    int d=exgcd(a,b,x,y);
    if(c%d!=0){puts("-1");return;} // 无解
    x*=c/d;y*=c/d;
    int p=b/d,q=a/d,k;
    if(x<0)k=ceil((1.0-x)/p),x+=p*k,y-=q*k; // 将x提高到最小正整数
    else k=(x-1)/p,x-=p*k,y+=q*k; // 将x降低到最小正整数
    if(y>0) // 存在x>0,y>0的解(正整数)
    {
        write((y-1)/q+1);   pc(' '); // 将y减到1的方案数即为解的个数
        write(x);           pc(' '); // 当前的x为最小正整数
        write((y-1)%q+1);   pc(' '); // 将y取到最小正整数
        write(x+(y-1)/q*p); pc(' '); // 将x提升到最大
        write(y);           pc(' '); // 特解即为y的最大值
    }else // 仅存在整数解
    {
        write(x);                       pc(' '); // 当前的x
        write(y+q*(int)ceil((1.0-y)/q));pc(' '); // 将y提高到正整数
    }
    pc('\n');
}

费马小定理

  1. 若 $p$ 为素数 , $\gcd(a,p)=1$ 则 $a^{p-1}\equiv 1 \pmod{p}$

  2. 若 $p$ 为素数 ,则对于任意整数 $a$ ,有 $a^p \equiv a \pmod{p}$

常用于 $O(\log n)$ 求解 $n$ 模素数 $p$ 意义下的逆元,详见逆元部分。


逆元

如果一个线性同余方程组 $ax \equiv 1 \pmod{b}$ ,则 $x$ 称为 $a \bmod b$ 的逆元,记作 $a^{-1}$

模意义下的乘法逆元不唯一。

如果 $b$ 不为素数,可能不存在逆元。

代码求解

P3811 【模板】乘法逆元

$O(n)$ 预处理 ,$O(1)$ 查询

求的是 $1,2,\cdots ,n$ 的模 $p$ 意义下的乘法逆元,$p$ 一般为质数。

为什么说一般呢?因为「 $p$ 为合数」会导致有些时候不存在逆元

如果正好存在逆元也是可以继续做的。

原理:

注意空间复杂度为 $O(n)$

inv[1]=1;
for(int i=2; i<=n; i++)
    inv[i]=(p-p/i)*inv[p%i]%p;

费马小定理 $O(\log p)$ 查询 $x$ 的逆元

原理:$x \equiv a^{p-2}\pmod{p}$ (根据费马小定理)

$p$ 需要是质数

cout << qpow(a,p-2) << '\n'; // 快速幂

$\text{Exgcd}~O(\log p)$ 查询 $x^{-1}$

求的是 $a$ 在模 $p$ 意义下的逆元,要求模数 $p$ 与 $a$ 互质。

int exgcd(int a,int b,int &x,int &y)
{
    int d=a;
    if(!b)x=1,y=0;
    else d=exgcd(b,a%b,y,x),y-=a/b*x;
    return d;
}
int solve(int a,int p)
{
	int x,y;
    exgcd(a,p,x,y);
    x=(x%p+p)%p;
    return x;
}

$\mathcal{O}(n)$ 预处理阶乘的逆元

因为 $n!=(n-1)! \times n$ ,所以 $((n-1)!)^{-1}=(n!)^{-1} \times n$ 。模数 $p$ 为质数

for(int i=1; i<=n; i++) fac[i] = fac[i-1] * i % p;
invf[n] = qpow(fac[n], p-2); // 费马小定理
for(int i=n-1; i>=1; i--) invf[i] = invf[i+1] * (i+1) % p;

请注意,如果 $p \le n$ ,这种方法就不可行了,因为 $n! \bmod p =0$ ,会出现不存在逆元的情况

这种时候一般采用上文线性求逆元的方法,只要求 $1\sim p-1$ 的就行了,即

// luogu P2675
for(int i=1; i<=n; i++) fac[i] = fac[i-1] * i % p;
for(int i=2; i<p; i++) inv[i] = (p-p/i) * inv[p%i] % p; // 注意是1 ~ p-1

欧拉函数

欧拉函数 $\varphi(n)$ 表示小于等于 $n$ 和 $n$ 互质的数的个数。

设 $n=\prod_{i=1}^{s}p_i^{k_i}$

常用性质:$\varphi(1) = 1,~\varphi(p) = p-1,~p\in\mathbb{P}$ ,以及 $n > 2$ 时,$\varphi(n)$ 为偶数

代码求解

$O(\sqrt{n})$ 求 $\varphi(n)$

板子题:poj2407

int Euler_phi(int n)
{
    int ans=n;
    for(int i=2; i<=n/i; 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;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n;
    while(cin>>n&&n!=0)
        cout << Euler_phi(n) << endl;

    return 0;
}

线性筛 $O(n)$ 求解

poj2478 (这个题要改成 $\varphi$ 的前缀和)

int phi[N],prime[N],pcnt;
bool ck[N];
void Euler()
{
    ck[1]=1;
    phi[1]=1;
    for(int i=2; i<=N-5; i++)
    {
        if(!ck[i])
        {
            prime[++pcnt]=i;
            phi[i]=i-1;
        }
        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)
        {
            int pos=i*prime[j];
            ck[pos]=1;
            if(i%prime[j])
            {
                phi[pos]=phi[i]*phi[prime[j]];
            }
            else
            {
                phi[pos]=phi[i]*prime[j];
                break;
            }
        }
    }
}

欧拉定理

若 $\gcd(a,m) = 1$ ,则 $a^{\varphi(m)} \equiv 1\pmod{m}$

当 $m$ 为素数时,根据欧拉函数的性质有 $a^{m-1}\equiv 1 \pmod{m}$ ,即费马小定理


扩展欧拉定理

应用

例题:

$1\le a,m\le 10^{12},1\le b\le 10^{20000000}$(这个是加强版的,朴素版只要int就行)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define gc() getchar()
int a,m,b,flag;
int Euler_phi(int n)
{
    int ans=n;
    for(int i=2; i<=n/i; 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;
}
int qpow(int a,int b,int p)
{
    int ans=1,base=a%p;
    while(b)
    {
        if(b&1)ans=(__int128)ans*base%p;
        base=(__int128)base*base%p;
        b>>=1;
    }
    return ans;
}
signed main()
{
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    scanf("%lld%lld",&a,&m);
    a%=m;
    int phi=Euler_phi(m);
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))
    {
        b=(b<<1)+(b<<3)+(ch^48);
        if(b>=phi)flag=1,b%=phi;ch=gc();
    }
    if(b>=phi)flag=1,b%=phi;
    if(flag)b+=phi;
    printf("%lld\n",qpow(a,b,m));
    return 0;
}

Lucas定理

对于质数 $p$ ,有

$\binom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor}$ 采用递归处理,后面的直接 $O(p)$ 预处理组合数

板子题:P3807 【模板】卢卡斯定理/Lucas 定理

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e5+15)
int Q,n,m,p;
int fac[N];
int qpow(int a,int b,int p)
{
	int ans=1,base=a;
	while(b)
	{
		if(b&1)ans=ans*base%p;
		base=base*base%p;
		b>>=1;
	}
	return ans;
}
// n 选 m
int C(int n,int m,int p)
{
	if(n<m)return 0;
	return fac[n]*qpow(fac[m],p-2,p)%p*qpow(fac[n-m],p-2,p)%p;
}
int lucas(int n,int m,int p)
{
	if(!m)return 1;
	return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;
}
signed main()
{
	cin >> Q;
	while(Q--)
	{
		cin >> n >> m >> p;
		fac[1]=fac[0]=1;
		for(int i=2; i<=n+m; i++)
			fac[i]=fac[i-1]*i%p;
		cout << lucas(n+m,m,p) << '\n'; // C(n+m,n)
	}
	return 0;
}

中国剩余定理

参考 中国剩余定理 & 扩展

中国剩余定理(CRT)可求解如下形式的一元线性同余方程组

其中 $p_1,p_2,\cdots,p_k$ 两两互质

板子题:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

算法原理很简单

  1. 计算 $P = \prod p_i$
  2. 记 $m_i = \frac{P}{p_i}$ ,$m_i^{-1}$ 为模 $p_i$ 意义下的乘法逆元,求 $c_i = m_im_i^{-1}$
  3. 方程组的唯一解 $x = \sum_{i=1}^{k}a_ic_i\pmod{P}$

证明就不讲了,中国剩余定理主要还是流程比较难记(确信)

时间复杂度 $\mathcal{O}(n \log V)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(15))

int a[N],p[N];
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;
}
int solve(int n, int P)
{
	int res = 0;
	for(int i = 1; i <= n; i++)
	{
		int m = P / p[i], x, y;
		exgcd(m, p[i], x, y); // m * x % p[i] = 1;
		res = (res + a[i] * m % P * x % P) % P;
	}
	return (res % P + P) % P;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	// freopen("check.in","r",stdin);
	// freopen("check.out","w",stdout);
	int n, P = 1; cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> p[i] >> a[i]; P = P * p[i];
	}
	cout << solve(n, P) << '\n';
	return 0;
}

扩展中国剩余定理

ExCRT 用于解决 $p_i$ 不一定互质的情况

设两个方程为 $x \equiv a_1 \pmod{p_1},~x \equiv a_2 \pmod{p_2}$ 。

可得

因此

令 $c = (a_2 - a_1)$ 。由裴蜀定理, 当且仅当 $\gcd(p_1,p_2)\mid c$ 时有解。

于是我们可以通过 exgcd 求出一对 $u_1,v_1$ ,满足 $p_1\cdot u_1 + p_2 \cdot v_1=g$ ($-p_2$ 与此实际上等价)

令 $g = \gcd(p_1,p_2)$ ,显然 $p_1u_1 \cdot \frac{c}{g} + p_2v_1 \cdot \frac{c}{g} = c$ ,则

为一组通解。于是我们求最小的解 $u_0 \equiv u_1 \cdot \frac{c}{g} \pmod{\frac{p_2}{g}}$ ,钦定 $u_0 > 0$ (都是为了方便和防止溢出)。

那么原方程就可以写作

这样我们就合并了两个式子,依此类推可以解决所有的式子。

板子题:P4777 【模板】扩展中国剩余定理(EXCRT)

时间复杂度 $\mathcal{O}(n \log V)$

代码:

// 2024年02月09日 03:38:48
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))

int exgcd(int a, int b, int &x, int &y)
{
    int d = a;
    if(!b) { x = 1, y = 0; }
    else { d = exgcd(b, a % b, y, x), y -= a / b * x;}
    return d;
}
int a[N], p[N];
int ExCRT(int n)
{
    int u, v, p1 = p[1], res = a[1];
    for(int i = 2; i <= n; i++)
    {
        int p2 = p[i], c = (a[i] - res % p2 + p2) % p2;
        int g = exgcd(p1, p2, u, v); assert(c % g == 0);
        int l = p2 / g; u = (__int128_t) u * (c / g) % l;
        res += u * p1; p1 *= l; res = (res % p1 + p1) % p1;
    }
    return (res % p1 + p1) % p1;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n; cin >> n;
    for(int i = 1; i <= n; i++) cin >> p[i] >> a[i];
    cout << ExCRT(n) << '\n';
    return 0;
}

莫比乌斯函数

$\mu$ 为莫比乌斯函数,定义为

代码求解

线性筛 $\mathcal{O}(n)$ 求 $\mu$

// 2023年07月04日 20:43:15
void Mobius(int n = N - 10)
{
    mu[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(!ck[i]) { prime[++pcnt] = i; mu[i] = -1; }
        for(int j = 1; j <= pcnt && i * prime[j] <= n; j++)
        {
            int pos = i * prime[j]; ck[pos] = true;
            if(i % prime[j]) { mu[pos] = -mu[i]; } else { mu[pos] = 0; break; }
        }
    }
}

莫比乌斯反演

应用

一个小转化(仅改变这两个相关和式,左侧其他的 $\Sigma$ 不变)

设 $d(x)$为 $x$ 的约数个数,则有


拉格朗日定理

设 $p$ 为素数,对于模 $p$ 意义下的整系数多项式

的同余方程 $f(x) \equiv 0 \pmod{p}$ 在模 $p$ 意义下至多有 $n$ 个不同解

应用

关于同余方程的引理

对于任意 $a$ ,至多有 $n$ 个不同的 $x$ 满足同余方程 $nx \equiv a \pmod{m}$

应用于抽象代数的定理中

在有限可交换群 $G$ 中,以下两个条件等价:

$G$ 是循环群。

对于任意一个元素 $a$ ,至多有 $n$ 个不同的元素 $x$ 满足条件 $x^n=a$ 。

结论:对于素数 $p$ ,模 $p$ 的简化剩余系 $Z_p^*$ 对于乘法构成的群是循环群


原根

:满足同余式 $a^n \equiv 1 \pmod{m}$ 的最小正整数 $n$ 存在,这个 $n$ 称作 $a$ 模 $m$ 的阶,记作 $\delta_m(a)$

在抽象代数中,这里的“阶”就是模 $m$ 缩剩余系关于乘法形成的群中,元素 $a$ 的阶。记号 $\delta$ 表示阶也只用于这个特殊的群。

下面的诸多性质可以直接扩展到抽象代数中阶的性质。

阶的性质

  1. $a,a^2,\dots,a^{\delta_m(a)}$ 模 $m$ 两两不同余

  2. 若 $a^n \equiv 1 \pmod{m}$ ,则 $\delta_m(a) \mid n$

  3. 若 $a^p \equiv a^q \pmod{m}$ ,则有 $p\equiv q \pmod{\delta_m(a)}$

  4. 设 $m \in \mathbb{N}^*,a,b \in \mathbb{Z} ,\gcd(a,m)=\gcd(b,m)=1$ ,则

    的充分必要条件是

  5. 设 $k\in \mathbb{N},m\in \mathbb{N}^*,a\in\mathbb{Z} ,\gcd(a,m)=1$ ,则

原根:设 $m\in \mathbb{N}^*,a \in \mathbb{Z} $ 若 $\gcd(a,m)=1$ ,且 $\delta_m(a) = \varphi(m)$ ,则称 $a$ 为模 $m$ 的原根

在抽象代数中,原根就是循环群的生成元。这个概念只在模 $m$ 缩剩余系关于乘法形成的群中有“原根”这个名字,在一般的循环群中都称作“生成元”。

并非每个模 $m$ 缩剩余系关于乘法形成的群都是循环群,存在原根就表明它同构于循环群,如果不存在原根就表明不同构。

原根判定定理:设 $m\ge 3,\gcd(a,m)=1$ ,则 $a$ 是模 $m$ 的原根的充要条件是,对于 $\varphi(m)$ 的每个素因数 $p$ ,都有 $a^{\frac{\varphi(m)}{p}}\not\equiv 1 \pmod{m}$

原根个数:若一个数 $m$ 有原根,则它的原根个数为 $\varphi(\varphi(m))$

原根存在定理:一个数 $m$ 存在原根当且仅当 $m=2,4,p^\alpha,2p^\alpha$ ,其中 $p$ 为奇素数,$\alpha \in \mathbb{N}^*$

  • 定理1:对于奇素数 $p$ ,$p$ 有原根
  • 定理2:对于奇素数 $p$ ,$\alpha \in \mathbb{N}^*$ ,$p^\alpha$ 有原根
  • 定理3:对于奇素数 $p$ ,$\alpha\in\mathbb{N}^*$ ,$2p^\alpha$ 的原根存在
  • 定理4:对于 $m\ne2,4$ ,且不存在奇素数 $p$ 及 $\alpha \in \mathbb{N}^*$ 使得 $m=p^\alpha,2p^\alpha$ ,模 $m$ 的原根不存在

二次剩余

对于二次同余方程 $x^2\equiv n \pmod{p}$ 有解,则称 $n$ 为 $p$ 的二次剩余,$x$ 为该二次同余方程的解

二次剩余 $n$ 就是一个二次项 $\bmod p$ 后的剩余,$n$ 不可以是 $p$ 的倍数

Legendre 符号(中间那个发音是g不是j)

Legendre 符号为完全积性函数

Euler 判别准则

对于奇素数

引理:令 $p$ 为素数和模 $p$ 意义下原根 $g$ 并令 $a\equiv g^k \pmod{p}$ ,那么 $x^2 \equiv a \pmod{p}$ 有解当且仅当 $k$ 为偶数

二次剩余和二次非剩余的数量

对于奇素数 $p$ 和集合 $\{1,2,\dots,p-1\}$ ,在模 $p$ 意义下二次剩余的数量等于二次非剩余的数量

引理:对于 $d \mid (p-1)$ 和奇素数 $p\in \mathbb{Z} $ ,$x^d \equiv 1 \pmod{p}$ 恰有 $d$ 个解


参考文献

[1] https://oi-wiki.org/math/number-theory/inverse/


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录