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

整除

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)
相关推荐
计算机安禾6 小时前
【C语言程序设计】第37篇:链表数据结构(一):单向链表的实现
c语言·开发语言·数据结构·c++·算法·链表·蓝桥杯
啊哦呃咦唔鱼6 小时前
LeetCode hot100-73 矩阵置零
算法
Zhansiqi6 小时前
dayy43
pytorch·python·深度学习
阿贵---6 小时前
C++构建缓存加速
开发语言·c++·算法
紫丁香6 小时前
pytest_自动化测试3
开发语言·python·功能测试·单元测试·集成测试·pytest
杰杰7986 小时前
Python面向对象——类的魔法方法
开发语言·python
Queenie_Charlie6 小时前
最长回文子串 V2(Manacher算法)
c++·算法·manacher算法
Evand J6 小时前
【MATLAB复现RRT(快速随机树)算法】用于二维平面上的无人车路径规划与避障,含性能分析与可视化
算法·matlab·平面·无人车·rrt·避障
一招定胜负6 小时前
机器学习+深度学习经典算法面试复习指南
深度学习·算法·机器学习
皮卡狮6 小时前
高阶数据结构:AVL树
数据结构·算法