数论基础:整除、同余与快速幂

整除

a对b向下取整:a // b

a对b向上取整:(a+b-1) // b

整除:a整除b,记为a|b,说明b%a == 0,即b是a的倍数,a是b的因数

性质:

如果a|b、blc,则alc

如果cla、c|b,则c|(ma+nb)

同余

设整数 m ≠ 0 ,且m | (a-b),则 a 同余于 b 模 m ,换句话说: a 和 b 在模 m 意义下同余

记为 : a ≡ b(mod m)

同余的性质:

自反性:a≡a(mod m)

对称性:若a≡b(mod m),则b≡a(mod m)

传递性:若a≡b(mod m),b≡c(mod m),则a≡c(mod m)

线性运算:若a≡b(mod m),c≡d(mod m),则: a±c≡b±d(mod m),axc≡bxd(mod m)

一般在求解问题时题目要求最终答案对mod求余,只需要每次运算均保持求余即可,

因为:

(a+b)%mod=((a%mod)+(b%mod))%mod

(axb)%mod=((a%mod)x(b%mod))%mod

上述性质保证只要运算中不出现除法,最终答案对mod求余,可以转换成每次运算均求余,答案不会发生变化。

GCD和LCM

GCD

假设a>b,a=bk+c,其中k是a/b的商,c是a/b的余数,其中c=a%b<b

则有:gcd(a,b)=gcd(b,c),即gcd(a,b)=gcd(b,a%b)

假设p= gcd(a,b),q=gcd(b,c),有pla,plb,q|b,q|c

由于c=a-bk⇒plc⇒ p是b,c的公因子 ⇒ q≥ p

由于a=bk+c⇒qla⇒q是a,b的公因子 ⇒ p≥q

最终:p=q

求最大公因数:

方法一(递归法):

python 复制代码
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

x, y = map(int, input().split())
print(gcd(x, y))

方法二(使用库函数):

from math import *

里面也有gcd函数,和上述功能相同,但是可以传递多个数字,计算多个数字的gcd

python 复制代码
from math import gcd
print(gcd(4, 6, 10))

LCM

gcd(a,b) * lcm(a,b) = ab

lcm(a,b) =ab / gcd(a,b)

python 复制代码
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

def lcm(a, b):
    return a * b / gcd(a, b)

例题

代码如下:(有一组数据运行超时)

python 复制代码
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

def lcm(a, b):
    return a * b / gcd(a, b)
n = int(input())
for _ in range(n):
    a0, a1, b0, b1 = map(int, input().split())
    ans = 0
    for x in range(1, int(b1 ** 0.5) + 1):
        if b1 % x != 0:
            continue
        if x % a1 == 0:
            if gcd(x, a0) == a1 and lcm(x, b0) == b1:
                ans += 1
        y = b1 // x
        if y != x:
            if y % a1 == 0:
                if gcd(y, a0) == a1 and lcm(y, b0) == b1:
                    ans += 1
    print(ans)

埃氏筛

代码如下:

python 复制代码
def get_prem(n):
    #prem存储所有素数
    prem = []
    #a表示是否已标记
    a = [0] * (n + 1)
    ##从2-n遍历所有数字,找到第一个未标记的数字,即为素数
    for i in range(2, n + 1):
        if a[i] == 0:
            prem.append(i)
            #将所有倍数全部标记
            for j in range(i + i, n + 1, i):
                a[j] = 1
    return prem

n = int(input())
prem = get_prem(n - 1)
print(*prem)
print(len(prem))

线性筛

埃式筛法缺点:一个合数被筛好几次 -- 能否一个合数只被筛1次呢?

线性筛:一个合数只被最小质因子筛一遍

python 复制代码
def get_prime(n):
    vis = [0] * (n + 1)      # vis[i]=1 表示合数
    prime = []                # 存储素数
   
    for i in range(2, n + 1):
        if vis[i] == 0:       # i是素数
            prime.append(i)
        
        # 用当前素数去筛掉 i * prime[j]
        for now_prime in prime:
            if i * now_prime > n: 
                break
            vis[i * now_prime] = 1   # 标记为合数
            
            # 关键:保证每个合数只被它的最小质因子筛掉
            if i % now_prime == 0:   # now_prime 是 i 的最小质因子
                break

快速幂

如何快速求解a^b%c

传统方法,循环b次,时间复杂度O(b)

快速幂:在log时间内求解

如何快速求解a^b%c

如果b为偶数,则计算a^(b/2),在此基础上计算平方

如果b为奇数,则计算a^(b/2),在此基础上计算平方再乘上a

求余操作在每次运算时均做一次求余答案不变

例题

代码如下:

python 复制代码
def ksm(b, p, k):
    if p == 0:
        return 1
    if p == 1:
        return b % k
    ans = ksm(b, p // 2, k)
    ans = ans * ans % k
    if p % 2 == 1:
        ans = ans * b % k
    return ans

b, p, k = map(int, input().split())
print(ksm(b, p, k))

唯一分解定理

唯一分解定理:也叫做算数基本定理,任意一个大于1的整数N,要么为质数,要么可以分解为有限个质数的乘积。

其中p1<p2 <...< pk,并且均为质数,q1,...,qk为正整数。

上述分解形式是一定存在且唯一的(可以看做质因子分解)

从小到大枚举所有质数x,然后如果x能整除,则不断除以x,直至除尽

需要预处理所有质数?

不需要,直接从小到大枚举可以整除的数字即可。

因为只要能整除,一定是质数,因为按照上述策略不可能存在整除合数的情况,合数一定会进一步分解为质数。

例题

代码如下:

python 复制代码
def solve(n):
    factor = []
    for i in range(2, n + 1):
        while n % i == 0:
            n //= i
            factor.append(i)
            if n == 1:
                break
    return factor

a, b = map(int, input().split())
for i in range(a, b + 1):
    factor = solve(i)
    print("{}=".format(i), end ='')
    print(*factor, sep = '*')

因子数、因子和

例如:15=3^1*5^1,因子为1,3,5,15,因子数为(1+1)(1+1)=4,因子和为(1+3)(1+5)=24。满足上述性质。

例如:36=2^2*3^2,因子为1,2,3,4,6,9,12,18,36,因子数为(2+1)(2+1)=9,因子和为(1+2+4)(1+3+9)=91。满足上述性质。

例题

python 复制代码
from collections import Counter
def solve(n):
    factor = []
    for i in range(2, n + 1):
        while n % i == 0:
            n //= i
            factor.append(i)
            if n == 1:
                break
    return factor

all_factor = []
for i in range(2, 101):
    all_factor += solve(i)
all_factor = Counter(all_factor)
ans = 1
for k, v in all_factor.items():
    ans *= v + 1
print(ans)
相关推荐
叩码以求索4 小时前
浅谈:前序遍历反转法求解N叉树的后序遍历
算法
lancyu4 小时前
关于多轮对话机器人的上下文Token优化和解决方案
人工智能·python·深度学习·机器学习·chatgpt·机器人·prompt
草莓橙子碗5 小时前
Claude 使用指南
开发语言·python
工具分享5 小时前
陪跑跟品爆单AI选品助手,高效跟品会赔本吗?
人工智能·python
COOLMO研究AI5 小时前
Python 如何实现 AI API 的提示词(Prompt)版本管理与热更新
人工智能·python·prompt
老郑聊AI业财智造5 小时前
Transformer 技术架构与源码分析
人工智能·python·深度学习·语言模型·架构·transformer·软件工程
北风toto5 小时前
中缀、前缀、后缀表达式
算法·软件设计师
程序员三藏5 小时前
自动化测试用例编写详解
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
听取WA声一片(无恶意)5 小时前
CSP-J/CSP-S 深度优先搜索(DFS)完全讲义
c++·算法·深度优先
码流怪侠6 小时前
2026年8月GitHub热榜深度拆解:Agent Skills席卷开源圈,一个“技能包“收割5万星
算法·程序员·github