基础数论算法刷题笔记

理论

最小公倍数、最大公约数

在这里插入图片描述
(a+b)%n = (a%n+b%n)%n
(ab)%n = (a%nb%n)%n
a≡2(mod n) —— a%n==2

lcm——最小公倍数
gcd——最大公约数

lcm(a,b) = a*b / gcd(a,b) 最小公倍数=两数的乘积除以最大公约数
但是写程序时应该是 a /gcd(a,b) *b 因为a*b可能会超出数据范围

在这里插入图片描述
例子:
36 21 36对21取模得到15
15 6 21对15取模得到6
3 0 15对6取模得到3,6对3取模得到0,所以3是最大公约数

以下是使用C++语言实现辗转相减法求最大公约数的代码,注释已经详细添加:

#include <iostream>
using namespace std;
int gcd(int a, int b) {
    // 如果a和b相等,直接返回a或b
    if (a == b) {
        return a;
    }
    // 如果a比b大,就让a减去b,否则让b减去a,
    // 然后再递归调用gcd函数,直到a和b相等为止
    if (a > b) {
        return gcd(a - b, b);
    } else {
        return gcd(a, b - a);
    }
}
int main() {
    int a, b;
    cout << "请输入两个整数:";
    cin >> a >> b;
    int result = gcd(a, b);
    cout << "最大公约数为:" << result << endl;
    return 0;
}

通过输入两个整数,程序会调用gcd函数求它们的最大公约数,并输出结果。

质数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int euler_phi(int n) {
    int phi = n;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            phi = phi / i * (i - 1);
            while (n % i == 0) {
                n /= i;
            }
        }
    }
    if (n > 1) {
        phi = phi / n * (n - 1);
    }
    return phi;
}

在这里插入图片描述

求n的质因子

#include <iostream>
#include <vector>
using namespace std;
vector<int> primeFactors(int n) {
    vector<int> factors;
    for (int i = 2; i * i <= n; i++) {
        while (n % i == 0) {
            factors.push_back(i);
            n /= i;
        }
    }
    if (n > 1) factors.push_back(n);
    return factors;
}
int main() {
    int n;
    cin >> n;
    vector<int> factors = primeFactors(n);
    for (int i = 0; i < factors.size(); i++) {
        cout << factors[i] << " ";
    }
    cout << endl;
    return 0;
}

上述代码中,primeFactors函数用于求一个数的质因子,它的实现方法是:从2开始循环,如果当前数n可以被i整除,则将i加入质因子数组,并将n除以i,直到n不能被i整除为止。当n>1时,说明n是一个质数,将它加入质因子数组中,最后返回质因子数组即可。

练习

[蓝桥杯 2022 省 A] 数的拆分

给定 T T T 个正整数 a i a_{i} ai,分别问每个 a i a_{i} ai 能否表示为 x 1 y 1 ⋅ x 2 y 2 x_{1}^{y_{1}} \cdot x_{2}^{y_{2}} x1y1x2y2 的形式,其中 x 1 , x 2 x_{1}, x_{2} x1,x2 为正整数, y 1 , y 2 y_{1}, y_{2} y1,y2 为大于等于 2 2 2 的正整数。

输入格式

输入第一行包含一个整数 T T T 表示询问次数。

接下来 T T T 行,每行包含一个正整数 a i a_{i} ai

输出格式

对于每次询问,如果 a i a_{i} ai 能够表示为题目描述的形式则输出 yes,否则输出 no

样例输入 #1

7
2
6
12
4
8
24
72

样例输出 #1

no
no
no
yes
yes
no
yes

提示

【样例说明】

4 , 5 , 7 4,5,7 4,5,7 个数分别可以表示为:

a 4 = 2 2 × 1 2 ; a 5 = 2 3 × 1 2 ; a 7 = 2 3 × 3 2 。 \begin{aligned} &a_{4}=2^{2} \times 1^{2} ; \\ &a_{5}=2^{3} \times 1^{2} ; \\ &a_{7}=2^{3} \times 3^{2} 。 \end{aligned} a4=22×12;a5=23×12;a7=23×32

【评测用例规模与约定】

对于 10 % 10 \% 10% 的评测用例, 1 ≤ T ≤ 200 , a i ≤ 1 0 9 1 \leq T \leq 200, a_{i} \leq 10^{9} 1T200,ai109;

对于 30 % 30 \% 30% 的评测用例, 1 ≤ T ≤ 300 , a i ≤ 1 0 18 1 \leq T \leq 300, a_{i} \leq 10^{18} 1T300,ai1018;

对于 60 % 60 \% 60% 的评测用例, 1 ≤ T ≤ 10000 , a i ≤ 1 0 18 1 \leq T \leq 10000, a_{i} \leq 10^{18} 1T10000,ai1018;

对于所有评测用例, 1 ≤ T ≤ 100000 , 1 ≤ a i ≤ 1 0 18 1 \leq T \leq 100000,1 \leq a_{i} \leq 10^{18} 1T100000,1ai1018

蓝桥杯 2022 省赛 A 组 I 题。

思路

  • 有一条引理,满足题目要求的数字也会满足 x 1 2 ∗ x 2 3 x_{1} ^{2} *x_{2}^{3} x12x23
  • 首先埃氏筛选法,找出质因子。因为1018,所以临界的条件是4000
  • 接着,如果给出的询问数本身就是可以成为平方数或者立方数,那么它一定满足条件(sqrt(n)2*13即可)
  • 如果询问的数 a a a 含有质因子,但是质因子 b b b 只出现了一次,就一定不符合条件(因为有质因子 b b b 的话,那么乘积为 a a a 就一定需要这个质因子,但是这个质因子只能出现一次,所以无法满足幂 ≥ 2 ≥2 2 的要求)

题解

#include<bits/stdc++.h>
using namespace std;

// Eratosthenes筛选法
const int N = 4000;
bool isprime[N + 1];
int prime[N / 3], pcnt = 0;
void esieve(void){
    memset(isprime, true, sizeof isprime);
    prime[pcnt++] = 2;
    for (int i = 3; i <= N; i += 2)
        if (isprime[i]) {
            prime[pcnt++] = i;
            for (int j = i + i; j <= N; j += i)
                isprime[j] = false;
        }
}

typedef long long LL;

bool judge(LL n){
    LL t = sqrt(n);
    if (t * t == n) return true;
    t = cbrt(n);
    if (t * t * t == n) return true;
    return false;
}

int main(){
    esieve();
    int t;
    LL a;
    scanf("%d", &t);
    while (t--) {
        scanf("%lld", &a);
        if (judge(a)) puts("yes");
        else {
            bool flag = true;
            for (int i = 0; i < pcnt; i++) {
                int cnt = 0;
                if (a % prime[i] == 0)
                    while (a % prime[i] == 0)
                        cnt++, a /= prime[i];
                if (cnt == 1) {
                    flag = false;
                    break;
                }
            }
            puts(flag && judge(a) ? "yes" : "no");
        }
    }
}

[蓝桥杯 2021 省 AB2] 完全平方数

一个整数 a a a 是一个完全平方数,是指它是某一个整数的平方,即存在一个 整数 b b b,使得 a = b 2 a=b^{2} a=b2

给定一个正整数 n n n,请找到最小的正整数 x x x,使得它们的乘积是一个完全平方数。

输入格式

输入一行包含一个正整数 n n n

输出格式

输出找到的最小的正整数 x x x

样例输入 #1

12

样例输出 #1

3

样例输入 #2

15

样例输出 #2

15

提示

对于 30 % 30 \% 30% 的评测用例, 1 ≤ n ≤ 1000 1 \leq n \leq 1000 1n1000,答案不超过 1000 1000 1000

对于 60 % 60 \% 60% 的评测用例, 1 ≤ n ≤ 1 0 8 1 \leq n \leq 10^{8} 1n108,答案不超过 1 0 8 10^{8} 108

对于所有评测用例, 1 ≤ n ≤ 1 0 12 1 \leq n \leq 10^{12} 1n1012,答案不超过 1 0 12 10^{12} 1012

蓝桥杯 2021 第二轮省赛 A 组 G 题(B 组 H 题)。

思路

  • 首先,找到题目中给出的数学规律:完全平方数需要质因子的偶数幂相乘其实自己在做题的时候就已经差不多要思考到这一步了
  • 所以,先将读入的 n n n 进行质因数分解。对于分解的每一个质数,如果它的指数为奇数,则 x x x 的因子就必须有这个质数

题解

#include <bits/stdc++.h>
using namespace std;
int main(){
	long long n,x=1;
	scanf("%lld",&n);
	for(long long i=2;i*i<=n;i++)
		if(n%i==0){
			int cnt=0;
			while(n%i==0){
				n/=i;
				cnt++;
			}
			if(cnt%2==1)x*=i;
		}
	if(n!=1)x*=n;
	printf("%lld\n",x);
}

[蓝桥杯 2017 省 AB] 包子凑数

小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 N N N 种蒸笼,其中第 i i i 种蒸笼恰好能放 A i A_i Ai 个包子。每种蒸笼都有非常多笼,可以认为是无限笼。

每当有顾客想买 X X X 个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有 X X X 个包子。比如一共有 3 3 3 种蒸笼,分别能放 3 3 3 4 4 4 5 5 5 个包子。当顾客想买 11 11 11 个包子时,大叔就会选 2 2 2 3 3 3 个的再加 1 1 1 5 5 5 个的(也可能选出 1 1 1 3 3 3 个的再加 2 2 2 4 4 4 个的)。

当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有 3 3 3 种蒸笼,分别能放 4 4 4 5 5 5 6 6 6 个包子。而顾客想买 7 7 7 个包子时,大叔就凑不出来了。

小明想知道一共有多少种数目是包子大叔凑不出来的。

输入格式

第一行包含一个整数 N N N ( 1 ≤ N ≤ 100 ) (1 \le N \le 100) (1N100)

以下 N N N 行每行包含一个整数 A i A_i Ai ( 1 ≤ A i ≤ 100 ) (1 \le A_i \le 100) (1Ai100)

输出格式

一个整数代表答案。如果凑不出的数目有无限多个,输出 INF

样例输入 #1

2  
4  
5

样例输出 #1

6

样例输入 #2

2  
4  
6

样例输出 #2

INF

提示

对于样例 1 1 1,凑不出的数目包括: 1 , 2 , 3 , 6 , 7 , 11 1,2,3,6,7,11 1,2,3,6,7,11

对于样例 2 2 2,所有奇数都凑不出来,所以有无限多个。

蓝桥杯 2017 省赛 A 组 H 题。

思路

  • 首先,如果给出的数不是互质的,即这组数存在一个公因数,所以这组数无论用什么数量关系所能表示的数都是涵盖这个公因数的那么肯定无法表示质数,所以不能表示的数便是INF
  • 后面类似完全背包问题,解法巧妙: f [ j ] ∣ = f [ j − a [ i ] ] f[j] |= f[j-a[i]] f[j]=f[ja[i]]|=是或运算,这个解法刚好能表达 k 1 x + k 2 y + … … k_{1}x+k_{2}y+…… k1x+k2y+……
    题解
#include <bits/stdc++.h>

using namespace std;
const int N = 10010;
int a[110], f[N], n; // f[i]表示i是否能被表示出来

int gcd(int a,int b) return a % b == 0 ? b : gcd(b, a % b);

int main(){
    cin>>n;
    for(int i=0; i < n; i++)
        cin>>a[i];
    int g = a[0];
    for(int i = 1; i < n; i++) 
        g = gcd(g, a[i]);
    if (g != 1) 
        cout << "INF" << endl;
    else   //完全背包问题
    {
        f[0]=1;
        for(int i = 0; i < n; i++)
            for(int j = a[i]; j < N; j++)
                f[j] |= f[j - a[i]];  //这里很巧妙
        int ans=0;
        for (int i = 1; i < N; i++)
                ans += !f[i];
        cout << ans << endl;
    }
}

[蓝桥杯 2016 省 AB] 最大比例

X 星球的某个大奖赛设了 M M M 级奖励。每个级别的奖金是一个正整数。

并且,相邻的两个级别间的比例是个固定值。

也就是说:所有级别的奖金数构成了一个等比数列。比如:

16 , 24 , 36 , 54 16,24,36,54 16,24,36,54

其等比值为: 3 / 2 3/2 3/2

现在,我们随机调查了一些获奖者的奖金数。

请你据此推算可能的最大的等比值。

输入格式

第一行为数字 N ( 0 < N < 100 ) N(0<N<100) N(0<N<100),表示接下的一行包含 N N N 个正整数。

第二行 N N N 个正整数 X i ( X i < 1 0 12 ) X_i(X_i<10^{12}) Xi(Xi<1012),用空格分开。每个整数表示调查到的某人的奖金数额。

输出格式

一个形如 A / B A/B A/B 的分数,要求 A A A B B B 互质。表示可能的最大比例系数。

测试数据保证了输入格式正确,并且最大比例是存在的。
样例输入 #1

3
1250 200 32

样例输出 #1

25/4

样例输入 #2

4
3125 32 32 200

样例输出 #2

5/2

样例输入 #3

3
549755813888 524288 2

样例输出 #3

4/1

提示

时限 3 秒, 256M。蓝桥杯 2016 年第七届省赛

蓝桥杯 2016 年省赛 A 组 J 题(B 组 J 题)。
思路

  • 观察题目给出的样例,会发现给出的数与数之间的比值应该是比例系数 k 1 / k 2 k_{1}/k_{2} k1/k2 i i i 次幂在做题的时候能想到这些,但是关于后面的实现没什么思路
  • 所以,只需要求出比值中分子分母的最大公约数
  • 由于我们事先不知每项比值的幂,因此无法通过辗转相除法来做,而求幂的最大公约数可以通过辗转相减法来做对于两个数,我们可以使用辗转相除法通过相除再取模的方式来求它们的最大公约数。但是,对于多个数,这种方法就不适用了。 例如,如果我们要求三个数12、18和30的最大公约数,我们无法通过简单的相除再取模的方式来进行计算。如果我们依次求出12和18的最大公约数、18和30的最大公约数,然后再求这两个最大公约数的最大公约数,这样的计算方式比较繁琐且容易出错。 相反,如果我们将这些数进行分解质因数,并找出它们的公共质因数,就可以直接求出它们的最大公约数。但是,对于多个数分解质因数的复杂度比较高,因此在这种情况下,使用辗转相减法可能更为方便。通过询问chatgpt得到的(我觉得能理解的解释)

题解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 110;
ll a[maxn], b[maxn], x[maxn];
ll gcd(ll a, ll b) return (!b)?a:gcd(b, a%b);
ll gcd_sub(ll a, ll b){
  if(a < b) swap(a, b);
  if(b == 0) return a;
  return gcd_sub(b, a-b);
}
int main(){
    int n;
    while(cin >> n){
      for(int i = 1; i <= n; i++) cin >> x[i];
      sort(x+1, x+1+n);
      int cnt = 0;
      for(int i = 2; i <= n; i++){
        if(x[i] != x[i-1]){
          cnt++;
          ll tmp = gcd(x[i], x[i-1]);
          a[cnt] = x[i-1] / tmp;
          b[cnt] = x[i] / tmp;
        }
      }
      ll son = a[1], parent = b[1];
      for(int i = 2; i <= cnt; i++){
        son = gcd_sub(son, a[i]);
        parent = gcd_sub(parent, b[i]);
      }
      cout << parent << "/" << son << endl;
    }
}

[蓝桥杯 2017 国 C] 小数第 n 位

我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数。

如果我们把有限小数的末尾加上无限多个 0 0 0,它们就有了统一的形式。

本题的任务是:在上面的约定下,求整数除法小数点后的第 n n n 位开始的 3 3 3 位数。

输入格式

一行三个整数: a a a b b b n n n,用空格分开。 a a a 是被除数, b b b 是除数, n n n 是所求的小数后位置( 0 < a , b , n < 1 0 9 0<a,b,n<10^9 0<a,b,n<109

输出格式

一行 3 3 3 位数字,表示: a a a 除以 b b b,小数后第 n n n 位开始的 3 3 3 位数字。

样例输入 #1

1 8 1

样例输出 #1

125

样例输入 #2

1 8 3

样例输出 #2

500

样例输入 #3

282866 999000 6

样例输出 #3

914

提示

时限 1 秒, 256M。蓝桥杯 2017 年第八届国赛

思路

  • 一开始我的思考是直接将 a ∗ p o w ( 10 , n ) a *pow(10,n) apow(10,n),然后去个位和小数位前2位。这个算法思想倒是没错,但是忽略了一个关键的问题:在 a ∗ p o w ( 10 , n ) a*pow(10,n) apow(10,n)之后,无法保证 a a a不超出数据范围所以,对于编程题,尽量减少大额的乘法
  • 所以,只能牺牲一定的时间,依次乘以1010,然后n-10因为给出的数据最大在109所以不会超出数据范围

题解

#include<bits/stdc++.h>
long long a,b,c,d,e;
int main(){
    scanf("%d%d%d",&a,&b,&c);
    a=a%b;
    while(c>10){    
        a*=1e10;
        c-=10;
        a=a%b;
    }
    for(int i=0;i<c+2;i++){
        a*=10;
        if(i>=c-1)e=a/b,printf("%d",e);
        a=a%b;
    }
    printf("\n");
}

总结

  1. 数论的题目,首先分析题目的意思,分解成数论相关的模型
  2. 接着,思路往自己学过的数论知识上靠——质因子、质数、最大公约数辗转相除法、辗转相乘法、公因数
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值