[AcWing蓝桥杯]之数论^ ^(C++题解)

目录

数论的一些模板:

(1)朴素筛法求素数

(2)线性筛--->>时间复杂度为O(N)(所以一般情况下都使用这个)

(3)分解数字为质因数

(4)约数的个数

(5)约数之和

(6)欧几里得算法(辗转相除)

(7)扩展欧几里得算法

公约数(欧几里得+更相减损)

等差数列

最大比例(等比数列)

线性同余方程+扩展欧几里得

         五指山

C循环

筛质数+分解质因数+组合计数

X的因子链

聪明的燕姿

正则问题


数论的一些模板:

这是我之前写的一些模板:详细的步骤过程在里面,这里只是核心步骤

数论之埃氏筛,线性筛,欧几里得算法,欧拉函数,欧拉定理,费马定理,快速幂,扩展欧几里得算法,线性同余方程,中国剩余定理(acwing模板篇)_lihua777的博客-CSDN博客_欧几里得定理acwing(8)https://blog.csdn.net/lihua777/article/details/122961428?spm=1001.2014.3001.5501

 根据蓝桥杯重点考察的回顾一下重点的数论算法

(1)朴素筛法求素数

核心:

内循环遍历完质因子的倍数,并将其标记为合数

#include<iostream>
using namespace std;
const int N = 1000010;
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 = i + i; j <= n; j += i) st[j] = 1;//间隔
		}
	}
}
int main() 
{
	int n;
	cin >> n;
	get_primes(n);
	cout << cnt;
}

(2)线性筛--->>时间复杂度为O(N)(所以一般情况下都使用这个)

核心:

思路与上面的朴素筛法差不多,都是标记倍数,不过线性筛每次时标记离自己最近那个倍数

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1000010;
int primes[N], cnt;
bool st[N];
void get_primes(int n)//12
{
	for (int i = 2; i <= n; i++) 
	{
		if (!st[i])//如果没有被标记过:证明时素数
		{
			primes[cnt++] = i;//primes[0]=2  primes[1]=3
		}
		for (int j = 0; primes[j] <= n / i; j++)
		{//st[4]=1  st[6]=1 st[9]=1
			st[primes[j] * i] = 1;//遍历素数数组,标记素数的i倍为true
			if (i % primes[j] == 0) break;//2%2==0  3%3==0 意思时遍历到最后primes数组的最后一个数了 break
		}
	}
}
int main() 
{
	int n;
	cin >> n;
	get_primes(n);
	cout << cnt;
 
	return 0;
}

(3)分解数字为质因数

核心:

公理:每个数字都可被拆分为质因数^指数的乘积形式(如下图所示)

 

#include<iostream>
using namespace std;
void divide(int n) 
{
	for (int i = 2; i <= n / i; i++) 
	{
		if (n % i == 0) 
		{//i一定是质数,因为此时2到i-1的质因子已经被除干净了 
			int s = 0;//计算次数 
			while (n % i == 0) 
			{
				n /= i;//i为什么是质数的原因
				s++;
			}
			printf("%d %d\n", i, s);
		}
	}
	if (n > 1) printf("%d %d\n", n, 1);//特判:当有一个比较大的质因子时
	cout << endl;
 
}
int main() 
{
	int n;
	cin >> n;
	while (n--) 
	{
		int x;
		cin >> x;
		divide(x);
	}
	return 0;
}

(4)约数的个数

核心:如下图

#include<iostream>
#include<unordered_map>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
 
int main() 
{
	int n;
	cin >> n;
	ll ans = 1;//因为要连乘,所以初始化为1
	unordered_map<int, int> hash;
	while (n--) 
	{
		int x;
		cin >> x;
		for (int i = 2; i <= x / i; i++) 
		{
			while (x % i == 0) 
			{
				x /= i;//分解质因数
				hash[i]++;//质因数的指数++(次数即指数)
			}
		}
		if (x > 1) hash[x]++;//保留最后一个质因数
	}
	for (auto &i : hash) ans = ans * (i.second + 1) % mod;//约数公式
	cout << ans;
    
    return 0;
}

(5)约数之和

核心:如下图

#include <iostream>
#include <algorithm>
#include <unordered_map>
 
using namespace std;
typedef long long LL;
const int N = 110, mod = 1e9 + 7;
 
int main()
{
    int n;
    cin >> n;
    unordered_map<int, int> primes;
 
    while (n--)
    {
        int x;
        cin >> x;
 
        for (int i = 2; i <= x / i; i++)
            while (x % i == 0)
            {
                x /= i;
                primes[i] ++;
            }
 
        if (x > 1) primes[x] ++;
    }
 
    LL res = 1;
 
    // ------------------约数之和公式-------------------------
    for (auto p : primes)
    {
        LL a = p.first, b = p.second;
        LL t = 1;
        while (b--) t = (t * a + 1) % mod; // while (b -- ) t = (t * a + 1) % mod; 是什么意思?
        res = res * t % mod;
    }
 
    cout << res << endl;
 
    return 0;
}

(6)欧几里得算法(辗转相除)

核心:

gcd(a,b)=gcd(b,a%b)

(1)当b==0时,0与任意一个数的最大公约数都是它本身

(2)当b!=0时就是gcd(a,b)=gcd(b,a%b)

#include<iostream>
#include<algorithm>
usin
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值