ACM 数学知识整理 (更新中)

一、素数

素数的定义:因数只有1和它本身的数

(1) 线性筛

int primes[N], cnt;
bool st[N];
void get_primes(int n)
{
    for(int i = 2; i <= n; ++i)
    {
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= n / i; ++j)
        {
            st[primes[j] * i] = 1;
            if(i % primes[j] == 0) break;
        }
    }
}

二、约数

(1) 分解质因数

对于一个大于1的整数n,n可以分解质因数为:
n = ∏ i = 1 k p i a i = p 1 a 1 ⋅ p 2 a 2 ⋅ ⋅ ⋅ p k a k n=\prod_{i=1}^{k}{p_i^{a_i}}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdot\cdot p_k^{a_k} n=i=1kpiai=p1a1p2a2pkak
试除法分解质因数:

for(int i = 2; i * i <= a; ++i)
{
    if(a % i == 0)
    {
        int s = 0;  //s为约数i的指数
        while(a % i == 0)
        {
            a /= i;
            s++;
        }
        //对i^s该项处理
        ......
    }
}
if(a > 1) 
{
    //说明本身含有一个大于根号a的质因子
    ......
}

(2) 约数个数公式

根据分解质因数所得式:
n = ∏ i = 1 k p i a i = p 1 a 1 ⋅ p 2 a 2 ⋅ ⋅ ⋅ p k a k n=\prod_{i=1}^{k}{p_i^{a_i}}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdot\cdot p_k^{a_k} n=i=1kpiai=p1a1p2a2pkak
则n的约数个数为:
f ( n ) = ∏ i = 1 k ( a i + 1 ) = ( a 1 + 1 ) ( a 2 + 1 ) ⋅ ⋅ ⋅ ( a k + 1 ) f(n)=\prod_{i=1}^{k}{(a_i+1)}=(a_1+1)(a_2+1)\cdot\cdot\cdot (a_k+1) f(n)=i=1k(ai+1)=(a1+1)(a2+1)(ak+1)

(3) 约数之和公式

( p 1 0 + p 1 1 + … + p 1 a 1 ) ( p 2 0 + p 2 1 + … + p 2 a 2 ) … ( p k 0 + p k 1 + … p k a k ) (p_1^0+p_1^1+…+p_1^{a_1})(p_2^0+p_2^1+…+p_2^{a_2})…(p_k^0+p_k^1+…p_k^{a_k}) (p10+p11++p1a1)(p20+p21++p2a2)(pk0+pk1+pkak)

O(logn)计算下式:
p 0 + p 1 + … + p k p^0+p^1+…+p^k p0+p1++pk

int sum(int p, int k)
{
    if(k == 1) return 1;
    if(k % 2 == 0) return (1 + qpow(p, k / 2)) * sum(p, k / 2) % mod;
    return (sum(p, k - 1) + qpow(p, k - 1)) % mod;
}

三、欧拉函数

定义:
ϕ ( n ) 表 示 : 1 到 n 中 与 n 互 质 的 数 的 个 数 \phi(n)表示:1到n中与n互质的数的个数 ϕ(n)1nn

a与b互质:a与b没有公因子

(1) 欧拉函数公式

先对N分解质因数得到:
N = ∏ i = 1 k p i a i = p 1 a 1 ⋅ p 2 a 2 ⋅ ⋅ ⋅ p k a k N=\prod_{i=1}^{k}{p_i^{a_i}}=p_1^{a_1}\cdot p_2^{a_2}\cdot\cdot\cdot p_k^{a_k} N=i=1kpiai=p1a1p2a2pkak
则欧拉函数公式为:
ϕ ( N ) = N ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋅ ⋅   ⋅ ( 1 − 1 p k ) \phi(N)=N(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k}) ϕ(N)=N(1p11)(1p21)(1pk1)
该公式证明:
根据容斥原理得出,所有和N互质的数的个数应该为:
N − N p 1 − N p 2 − ⋅ ⋅ ⋅   − N p k + N p 1 p 2 + N p 1 p 3 + ⋅ ⋅ ⋅   + N p i p j + ⋅ ⋅ ⋅   − N p 1 p 2 p 3 − N p 1 p 2 p 4 − ⋅ ⋅ ⋅ + N p 1 p 2 p 3 p 4 − ⋅ ⋅ ⋅ N-\frac{N}{p_1}-\frac{N}{p_2}-\cdot\cdot\cdot\,-\frac{N}{p_k}+ \\\frac{N}{p_1p_2}+\frac{N}{p_1p_3}+\cdot\cdot\cdot\,+\frac{N}{p_ip_j}+\cdot\cdot\cdot\, \\-\frac{N}{p_1p_2p_3}-\frac{N}{p_1p_2p_4}-\cdot\cdot\cdot+\frac{N}{p_1p_2p_3p_4}-\cdot \cdot \cdot Np1Np2NpkN+p1p2N+p1p3N++pipjN+p1p2p3Np1p2p4N+p1p2p3p4N
以此类推,上式化简得该公式。

(2) 筛法求欧拉函数

可根据线性筛,在筛出素数的同时筛出欧拉函数。

首先,根据欧拉函数的定义,一个素数p的欧拉函数即1~p中与p互质的数的个数,因此有下式:

ϕ ( p ) = p − 1 , p 是 素 数 \phi(p) = p - 1, p是素数 ϕ(p)=p1,p
在线性筛中,对于一个合数N来说,从欧拉公式出发,先对N进行质因数分解,公式计算得:
ϕ ( N ) = N ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋅ ⋅   ⋅ ( 1 − 1 p k ) \phi(N)=N(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k}) ϕ(N)=N(1p11)(1p21)(1pk1)
我们可以发现公式中,欧拉函数的值与分解质因数后的指数无关,而N乘上一个质因子 p j p_j pj后,就要分两种情况讨论:

p j 是 N 约 数 , 即 N    %    p j = 0    : p_j是N约数,即N\;\%\;p_j=0\;: pjNN%pj=0

此时 p j ∈ { p 1 , p 2 , . . . , p k } p_j \in \{p_1,p_2,...,p_k\} pj{p1,p2,...,pk}
因此有:
ϕ ( N × p j ) = N ⋅ p j ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋅ ⋅   ⋅ ( 1 − 1 p k ) = ϕ ( N )   ⋅ p j \phi(N\times p_j)=N\cdot p_j(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k})=\phi(N)\,\cdot p_j ϕ(N×pj)=Npj(1p11)(1p21)(1pk1)=ϕ(N)pj

p j 不 是 N 约 数 , 即 N    %    p j ≠ 0    : p_j不是N约数,即N\;\%\;p_j \not ={0}\;: pjNN%pj=0

此时 p j ∉ { p 1 , p 2 , . . . , p k } p_j \notin \{p_1,p_2,...,p_k\} pj/{p1,p2,...,pk}
因此有:
ϕ ( N × p j ) = N ⋅ p j ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋅ ⋅   ⋅ ( 1 − 1 p k ) ⋅ ( 1 − 1 p j ) = ϕ ( N )   ⋅ ( p j − 1 ) \phi(N\times p_j)=N\cdot p_j(1-\frac{1}{p_1})(1-\frac{1}{p_2}) \cdot\cdot\,\cdot(1-\frac{1}{p_k})\cdot(1-\frac{1}{p_j})=\phi(N)\,\cdot (p_j - 1) ϕ(N×pj)=Npj(1p11)(1p21)(1pk1)(1pj1)=ϕ(N)(pj1)
因此利用线性筛同时筛出欧拉函数。

int primes[N], cnt;
int phi[N];
bool st[N];

void get_eulers(int n)
{
    phi[1] = 1;
    for(int i = 2; i <= n; ++i)
    {
        if(!st[i])
        {
            primes[cnt++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; primes[j] <= n / i; ++j)
        {
            st[primes[j] * i] = 1;
            if(i % primes[j] == 0)
            {
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
            phi[i * primes[j]] = phi[i] * (primes[j] - 1);
        }
    }
}

四、快速幂

快速幂模板

typedef long long ll;

ll qpow(ll a, ll b, ll p)
{
    ll res = 1;
    while(b)
    {
        if(b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

快速幂求逆元(费马小定理)

乘法逆元的定义:
b 和 m 互 质 , a 整 除 b ,    a / b ≡ a × x    ( m o d    m ) b和m互质,a整除b,\;a/b\equiv a\times x\;(mod\;m) bm,ab,a/ba×x(modm)

我们希望不做除法,因为取余数对于除法是很麻烦的一件事。我们希望把除法变成乘法,如果能找到一个数 x x x,满足 a / b    m o d    m = a × x    m o d    m a/b\;mod \;m = a\times x\;mod\;m a/bmodm=a×xmodm,那么我们就把 x x x叫做 b b b的模 m m m的逆元,记作: a / b    ≡ a ⋅ b − 1    ( m o d    m ) a/b\;\equiv a \cdot b^{-1}\;(mod\;m) a/bab1(modm)

逆元有什么样的性质?

b ⋅ b − 1 ≡ 1    ( m o d    m ) b\cdot b^{-1}\equiv 1\;(mod\;m) bb11(modm)

求逆元的问题可以转化为什么?

找到一个 x x x,使得 b ⋅ x ≡ 1    ( m o d    m ) b\cdot x\equiv 1\;(mod\;m) bx1(modm),即 b ⋅ x    m o d    m = 1 b\cdot x\;mod\;m=1 bxmodm=1

费马小定理:

p 是 素 数 , b p − 1 ≡ 1    ( m o d    p ) p是素数,b^{p-1}\equiv 1\;(mod\;p) pbp11(modp)

根据费马小定理,可知
b ⋅ b p − 2 ≡ 1      ( m o d      m ) b\cdot b^{p-2} \equiv 1\;\;(mod\;\;m) bbp21(modm)
所以 b p − 2 b^{p-2} bp2 b b b 的逆元

下面考虑什么时候 b b b的逆元不存在?显然,当b是p的倍数时,逆元不存在。

接下俩,就可以直接通过快速幂直接求出 b b b的逆元。

int b, p;
cin >> b >> p;
if(b % p) cout << qpow(b, p - 2, p) << endl;
else cout << "impossible" << endl;

逆元可以用来干啥?

观察下列四个式子:

(a + b) % p = (a % p + b % p) % p 成立

(a - b) % p = (a % p - b % p) % p 成立

(a * b) % p = (a % p * b % p) %p 成立

(a / b) % p = (a % p / b % p) % p 不成立

只有在除法的时候我们无法通过直接展开取模最后再取模的方式来得到最后的结果,通过逆元,就可以将上式化为:
( a    /    b )    %    p = ( a    ⋅    b − 1 )    %    p (a\;/\;b)\;\%\;p = (a\;\cdot\;b^{-1})\;\%\;p (a/b)%p=(ab1)%p
这样就可以通过上述方法来求了。

五、拓展欧几里得算法

问题描述:

给定 n n n 对正整数 a i , b i a_i,b_i ai,bi,对于每对数,求出一组 x i , y i x_i,y_i xi,yi,使其满足 a i × x i + b i × y i = g c d ( a i , b i ) a_i×x_i+b_i×y_i=gcd(a_i,b_i) ai×xi+bi×yi=gcd(ai,bi)

求 出 一 组 x , y 满 足 : a x + b y = g c d ( a , b ) 求出一组x,y满足:ax+by=gcd(a,b) x,yax+by=gcd(a,b)

裴蜀定理:

对于任意正整数 a , b a,b a,b,一定存在非零整数 x , y x, y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

a x + b y = d ax+by=d ax+by=d d d d 一定是 g c d ( a , b ) gcd(a,b) gcd(a,b) 的倍数,即a和b能凑出来的最小正整数就是a和b的最大公因数

拓展欧几里得算法就是构造一组 x , y x,y x,y,使其满足 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),我们可以根据欧几里得算法递归的过程中记录下来其系数,推导过程如下:

b = 0 b=0 b=0 时, a a a b b b 的最大公因数就是 a a a
因此构造 a x + b y = a ax+by=a ax+by=a,即 x = 1 , y = 0 x=1,y=0 x=1,y=0

b ≠ 0 b\neq 0 b=0时,对于 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),根据欧几里得算法, g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)

因此要构造 b y + ( a % b ) x = g c d ( a , b ) by+(a\%b)x=gcd(a,b) by+(a%b)x=gcd(a,b)

a % b = a − f l o o r ( a b ) a\%b=a-floor(\frac{a}{b}) a%b=afloor(ba),代入可得, a x + b ( y − f l o o r ( a b ) ⋅ x ) = g c d ( a , b ) ax+b(y-floor(\frac{a}{b})\cdot x) = gcd(a,b) ax+b(yfloor(ba)x)=gcd(a,b)

因此拓展欧几里得算法如下:

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

(1) 求解线性同余方程

给定 a , b , m a,b,m a,b,m,解线性同余方程: a x ≡ b    ( m o d    m ) ax\equiv b\;(mod\;m) axb(modm),判断是否有解,有解求出 x x x

问题等同于求解一个 x x x a x    m o d    m = b    m o d    m ax\;mod\;m=b\;mod\;m axmodm=bmodm

因此, a x ax ax等于 m m m的若干倍加上 b    m o d    m b\;mod\;m bmodm

a x ≡ b    ( m o d    m ) ⇔ ax\equiv b\;(mod\;m) \Leftrightarrow axb(modm)

∃    y ∈ Z , s . t .    a x = m y + b \exist\; y \in Z,s.t.\;ax=my+b yZ,s.t.ax=my+b

⇒ a x − m y = b , b ∈ Z \Rightarrow ax-my=b,b\in Z axmy=bbZ

y ′ = − y y^{'}=-y y=y

⇒ a x + m y ′ = b , b ∈ Z \Rightarrow ax+my^{'}=b,b\in Z ax+my=bbZ

即解上面这个方程:

只要 ( a , m )    ∣    b (a,m)\;|\;b (a,m)b b b b能整除 a a a m m m的最大公约数,那么它就有解,如果b不能整除 a a a m m m的最大公约数,它就无解。

解 a x + m y ′ = g c d ( a , m ) , b = k × g c d ( a , m ) , 即 倍 数 为 k , g c d ( a , m ) = d , 则 k = b d 解ax+my^{'}=gcd(a,m),b=k\times gcd(a,m),即倍数为k,gcd(a,m)=d,则k=\frac{b}{d} ax+my=gcd(a,m)b=k×gcd(a,m)kgcd(a,m)=dk=db

k × ( a x + m y ′ ) = k × d k\times(ax+my^{'})=k\times d k×(ax+my)=k×d

因此通过拓展欧几里得算法即求解出一组 x , y x,y x,y k x 就 是 答 案 kx就是答案 kx

typedef long long ll;
//ax = b (mod m)
int x, y, a, b;
cin >> a >> m >> b;
int d = exgcd(a, m, x, y);
if(b % d) printf("impossible");
int res = (ll)b / d * x % m;

(2) 拓展欧几里得算法求逆元

求乘法逆元实际上就是解一个线性同余方程:
b x ≡ 1    ( m o d    m ) bx\equiv 1\;(mod\;m) bx1(modm)

即: b x + m y = 1 bx+my=1 bx+my=1

b b b的逆元:

int mod_reverse(int b, int m)
{
    int d, x, y;
    d = exgcd(b, m, x, y);
    if(d == 1)
        return (x % m + m) % m;
    else
        return -1;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫红 染 秋季

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值