Acwing提高课--数论

本文详细介绍了数论中的重要概念和算法,包括质数的性质、埃氏筛和线性筛两种筛法、约数计算、组合数的求解、卡特兰数、欧拉函数、扩展欧几里得算法及其应用。此外,还涵盖了博弈论、容斥原理、Mobius函数和高斯消元等数学工具在信息技术中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数论–提高课

质数

性质

  1. 1-n质数个数:N/lnN
  2. 分解质因数:N!中p的次数–N/p + N/p^2 + … + N/p^x[下取整]
  3. 非全等数列不可能既是等差又是等比

埃氏筛:

复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

代码:

void get_primes1(){
    for(int i=2;i<=n;i++)
    {
        if(!st[i])
        {
            primes[cnt++]=i;
            for(int j=i;j<=n;j+=i) st[j]=true;//利用质数倍增将合数筛掉
        }
    }
}

线性筛(欧筛):

代码:– O ( n ) O(n) O(n)

  • 利用质因子 p r i m e prime prime i ∗ p r i m e i * prime iprime筛掉
void get_primes(){
    //外层从2~n迭代,因为这毕竟算的是1~n中质数的个数,而不是某个数是不是质数的判定
    for(int i = 2;i <= n;i ++)
    {
        if(!st[i]) primes[cnt ++]=i;
        
        for(int j = 0; primes[j] * i <= n; j ++)
        {//primes[j]*i<=n,把大于n的合数都筛了就没啥意义了
            st[primes[j] * i] = true;//用最小质因子去筛合数

            //1)当i%primes[j]!=0时,说明此时遍历到的primes[j]不是i的质因子,那么只可能是此时的primes[j]<i的
            //最小质因子,所以primes[j]*i的最小质因子就是primes[j];
            //2)当有i%primes[j]==0时,说明i的最小质因子是primes[j],因此primes[j]*i的最小质因子也就应该是
            //prime[j],之后接着用st[primes[j+1]*i]=true去筛合数时,就不是用最小质因子去更新了,因为i有最小
            //质因子primes[j]<primes[j+1],此时的primes[j+1]不是primes[j+1]*i的最小质因子,此时就应该
            //退出循环,避免之后重复进行筛选。
            if(i % primes[j] == 0) break;
        }
    }

}

约数

性质

  1. 则N的约数个数为:

设 N 分解质因子为: N = p 1 a 1 ∗ p 2 a 2 ∗ ⋯ ∗ p n a n 其中 p 1 , p 2 , ⋯   , p n 为 N 的质因子 则 N 的约数个数为: f ( N ) = ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ ⋯ ∗ ( a n + 1 ) 设N分解质因子为: \\N = p_1^{a_1} * p_2^{a_2} *\cdots*p_n^{a_n}\\其中p_1,p_2,\cdots,p_n为N的质因子\\ 则N的约数个数为:f(N) = (a_1 + 1) * (a_2 + 1) * \cdots* (a_n + 1) N分解质因子为:N=p1a1p2a2pnan其中p1,p2,,pnN的质因子N的约数个数为:f(N)=(a1+1)(a2+1)(an+1)

  1. **1-N所有数的约数个数之和:**NlogN
    ∑ i = 1 N f ( i ) = N 1 + N 2 + N 3 + ⋯ + N N = N ∗ ( 1 + 1 2 + 1 3 + ⋯ + 1 N ) ≈ N l o g N \sum_{i = 1}^{N}{f(i)} = \frac{N}{1} + \frac{N}{2} + \frac{N}{3} + \cdots+\frac{N}{N} \\=N*(1 + \frac{1}{2} + \frac{1}{3} + \cdots + \frac{1}{N}) \\ \approx NlogN i=1Nf(i)=1N+2N+3N++NN=N(1+21+31++N1)NlogN

  2. int范围内f(i)__max = 1600.

代码

for(int i = 2; i <= num / i; i++ )
        {
            while(num % i == 0)
            {
                num /= i;
                primes[i] ++;
            }
        }
        //最后num!=1,那么num此时为一个质数
        if(num > 1) primes[num] ++;
    }

    LL ans = 1;
    for(auto p : primes)    ans = (p.second + 1)  * ans % mod;

求组合数

在这里插入图片描述

1.	void init()//阶乘求逆元
	{
		infac[0] = fac[0] = 1;
		rep(i, 1, N - 10)
		{
			fac[i] = fac[i - 1] * i % mod;
			infac[i] = qmi(fac[i], mod - 2);
			//infac[i] = infac[i - 1] * qmi(i, mod - 2) % mod;  这个也可以求阶乘逆元
		}
	}
2.	void init()//递推式
	{
		C[0][0] = 1;
		rep(i, 1, 60)
			rep(j, 0, i) 
			{
				C[i][j] = C[i - 1][j];
				if(j) C[i][j] += C[i - 1][j - 1];
			}
	}

卡特兰数

任意前缀0的个数>=1的个数–卡特兰数!!

ps:看见第三项为5是大概率就是卡特兰数

C 2 n n − C 2 n n − 1 = C 2 n n n + 1 C_{2n}^{n}-C_{2n}^{n-1} = \frac{C_{2n}^{n}}{n+1} C2nnC2nn1=n+1C2nn

欧拉函数

Euler定理

a , m ∈ N + a, m \in \mathbb{N^+} a,mN+ 且 gcd(a,m) = 1

则有: a ϕ m ≡ 1 ( % m ) a^{\phi_{m}} \equiv 1 (\%m) aϕm1(%m)

这个可以延伸出费马小定理。

性质
欧拉函数 ϕ ( n ) : 1 − n 中与 n 互质的数的个数。 N = p 1 a 1 ∗ p 2 a 2 ∗ ⋯ ∗ p n a n − − p i 为质因子 ϕ ( N ) = N ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) ∗ ⋯ ∗   ( 1 − 1 p n ) 欧拉函数\phi(n):1-n中与n互质的数的个数。 \\ N = p_1^{a_1} * p_2^{a2}*\cdots*p_n^{a_n}--p_i为质因子 \\\phi(N) = N * (1 - \frac{1}{p_1})*(1-\frac{1}{p_2})*\cdots*\ (1-\frac{1}{p_n}) 欧拉函数ϕ(n)1n中与n互质的数的个数。N=p1a1p2a2pnanpi为质因子ϕ(N)=N(1p11)(1p21) (1pn1)
Code

void init(int n)
{
    //因为线性筛能将1-n所有数筛且仅筛一遍,故在线性筛的基础上求每个数的欧拉函数
    for(int i = 2; i <= n; i ++)
    {
        if(!st[i])//i此时为质数,则互质的数为i-1个
        {
            primes[cnt ++] = i;
            euler[i] = i - 1;
        }
        for(int j = 0; primes[j] * i <= n; j ++)//因为primes[j] * i > n,所以将primes[j] * i筛去也没意思
        {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) //此时primes[j]是i的最小质因子,那么也是primes[j] * i的最小质因子
            {
                euler[i * primes[j]] = euler[i] * primes[j]; 
                break;
            }
            //此时primes[j]不是i * primes[j] 的质因子,所以在i的欧拉函数上又多了一个质因子
            euler[i * primes[j]] = euler[i] * (primes[j] - 1);
        }
    }
}

扩展欧几里得

应用

  1. 给a,b以及 d = gcd(a,b)求x0,y0使得:ax0 + by0 = d

  2. 求x,y的通解:

    x = x 0 + k ∗ b / d x = x_{0} + k * b/d x=x0+kb/d

    y = y 0 + k ∗ a / d y=y_{0} + k * a/d y=y0+ka/d

    –> x最小正整数解 = ( x 0 % ( b / d ) + ( b / d ) ) % ( b / d ) (x_{0} \% (b/d) + (b/d)) \% (b/d) (x0%(b/d)+(b/d))%(b/d)

证明

基本算法:对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。

我们观察到:欧几里德算法停止的状态是: a= gcd , b = 0 ,那么,这是否能给我们求解 x y 提供一种思路呢?因为,这时候,只要 a = gcd 的系数是 1 ,那么只要 b 的系数是 0 或者其他值(无所谓是多少,反正任何数乘以 0 都等于 0 但是a 的系数一定要是 1),这时,我们就会有: a1 + b0 = gcd

当然这是最终状态,但是我们是否可以从最终状态反推到最初的状态呢?

假设当前我们要处理的是求出 a 和 b的最大公约数,并求出 x 和 y 使得 ax + by= gcd ,而我们已经求出了下一个状态:b 和 a%b 的最大公约数,并且求出了一组x1 和y1 使得: bx1 + (a%b)y1 = gcd , 那么这两个相邻的状态之间是否存在一种关系呢?

我们知道: a%b = a - (a/b)*b(这里的 “/” 指的是整除,例如 5/2=2 , 1/3=0),那么,我们可以进一步得到:

    gcd = b*x1 + (a-(a/b)*b)*y1

           = b*x1 + a*y1 – (a/b)*b*y1

           = a*y1 + b*(x1 – a/b*y1)

对比之前我们的状态:求一组 x 和 y 使得:a*x + b*y = gcd ,是否发现了什么?

这里:

    x = y1

    y = x1 – a/b*y1

以上就是扩展欧几里德算法的全部过程

作者:incra
链接:https://www.acwing.com/blog/content/15760/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

扩欧求逆元:

a x ≡ 1 ( m o d m ) ax≡1 (mod m) ax1(modm),这里,我们称 x 是 a 关于 m 的乘法逆元。
利用扩展欧几里得算法,等价为: ax + my = 1。
我们发现当gcd(a , m) != 1 的时候是没有解的。

这也是 ax + by = c 有解的充要条件: c % gcd(a , b) == 0
一般,我们能够找到无数组解满足条件,但是一般是让你求解出最小的那组解,怎么做?我们求解出来了一个特殊的解 x0 那么,我们用 ( x 0 % m + m ) % m (x_0 \% m+m)\%m (x0%m+m)%m其实就得到了最小正整数解了。(x 的通解是 x0 + mt

Code:

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;
}

同余方程组(中国剩余定理)

形如:
KaTeX parse error: Invalid size: 'ai之间要互质!!' at position 111: …x_n (\%a_n) \\ [̲a̲i̲之̲间̲要̲互̲质̲!̲!̲]̲
则:
设 M = a 1 ∗ a 2 ∗ a 3 ∗ ⋯ ∗ a n ; 令 M i = M a i , t i 是 M i 关于 a i 的逆元即: M i ∗ t i ≡ 1 ( % a i ) 则: N = ∑ i = 1 n x i ∗ M i ∗ t i 设 M = a_1 * a_2 * a_3 * \cdots * a_n; \\ 令 M_i = \frac{M}{a_i},t_i是M_i关于a_i的逆元即: M_i*t_i\equiv1(\%a_i) \\ 则:N = \sum_{i = 1}^{n}{x_i*M_i*t_i} M=a1a2a3an;Mi=aiM,tiMi关于ai的逆元即:Miti1(%ai)则:N=i=1nxiMiti

博弈论

关于博弈论能用dp推出来的原因

因为: 博弈论的某个状态一定可以由前面的状态得到, 一定相当于是有向无环图即:拓扑图. 故可以使用记忆化搜索(DP)得到.

容斥原理

trick:容斥原理可以用二进制枚举,看1的个数,来确定正负号

$$
|A\cup B\cup C\cup…\cup K| = |A| + |B| + |C| +…+ |K| \-|A\cap B| - |A\cap C|-…-|A\cap K|\
+|A\cap B\cap C| + … +
\…

\ 即:多个集合的并集=奇数个集合交集的和减去偶数个集合交集的和
$$

Mobius函数

在这里插入图片描述

Code:

void init(int n)
{
    mobius[1] = 1;
    for(int i = 2; i <= n; i ++)
    {
        if(!st[i])
        {
            primes[cnt ++] = i;
            mobius[i] = -1;//质数:k=1
        }
        for(int j = 0; primes[j] * i <= n; j ++)
        {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0)
            {
                //i有一个primes[j]故i*primes[j]:k = 2
                mobius[i * primes[j]] = 0;
                break;
            }
            //在i的基础上多了一个primes[j]
            mobius[i * primes[j]] = mobius[i] * -1;
        }
    }
    for(int i = 1; i <= n; i ++)    sum_mobius[i] = sum_mobius[i - 1] + mobius[i];
}

高斯消元

在n3的时间内求解n元线性方程组

变为上三角矩阵步骤:

枚举列:

  1. 找主元(当前列绝对值最大的那个)
  2. 交换(将主元的行换到最上面)
  3. 归一(将这个主元系数化为1)
  4. 消(用这个化为1的主元将这一列的系数消去)

5:最后用上三角矩阵求解未知数

代码

int gauss()
{
    //枚举列
    int c, r;
    for(c = 0, r = 0; c < n; c ++)
    {
        //找主元
        int t = r;
        for(int i = r; i < n; i ++)
            if(fabs(f[i][c]) > fabs(f[t][c])) t = i;
        
        //该列最大主元为0
        if(eps > fabs(f[t][c])) continue;
        
        //交换:将主元行换为第r行
        for(int i = c; i <= n; i ++) swap(f[r][i], f[t][i]);
        
        //归一:将主元系数消为1
        for(int i = n; i >= c; i --) f[r][i] /= f[r][c];
        
        //消:利用归一后的主元将该行下面的这列系数全消为0
        for(int i = r + 1; i < n; i ++)
            if(fabs(f[i][c]) > eps) 
                for(int j = n; j >= c; j --)
                    f[i][j] -= f[i][c] * f[r][j];
        r ++;
    }//枚举完列之后,矩阵就变为上三角矩阵
    
    if(r < n) 
    {
        for(int i = r; i < n; i ++)
            if(f[i][n] > eps) return 2;//无解: 0 = 非0
        return 1;//有无穷多解
    }
    
    //求解:将上三角变为答案
    for(int i = n - 1; i > 0; i --)
        for(int j = i - 1; j >= 0; j --)
            f[j][n] -= f[j][i] * f[i][n];
    return 0;//有唯一解
}

矩阵乘法板子

int N = ;
struct matrix
{
	int n, m;//矩阵的行列
	LL num[N + 5][N + 5];
	void clear()
	{
		mem(num, 0);
	}
	void print()
	{
		rep(i, 0, n - 1)
		{
			rep(j, 0, m - 1) cout << num[i][j] << ' ';
			cout << endl;
		}
	}
};

matrix operator + (matrix a, matrix b)
{
	rep(i, 0, a.n - 1)
		rep(j, 0, a.m - 1) a.num[i][j] = (a.num[i][j] + b.num[i][j]) % mod;
	return a;
}

matrix operator * (matrix a, matrix b)
{
	matrix ans;
	ans.n = a.n, ans.m = b.m;
	ans.clear();
	rep(i, 0, a.n - 1)
		rep(j, 0, b.m - 1) 
			rep(k, 0, a.m - 1)
				ans.num[i][j] = (ans.num[i][j] + a.num[i][k] * b.num[k][j] % mod) % mod;
	return ans;
}

matrix qmi(matrix a, LL k)
{
	matrix res;
	res.n = a.n, res.m = a.m;
	res.clear();
	rep(i, 0, a.n - 1) res.num[i][i] = 1;
	while(k)
	{
		if(k & 1) res = res * a;
		k >>= 1;
		a = a * a;
	}
	return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值