东方博宜OJ 训练计划:数学知识及编程应用 习题集

啊啊啊,好久没发过题解啦,望各位大佬 RP++

食用OJ : 东方博宜
网站:东方博宜

第一部分:素数

这里想必大家都很了解了,就不详细介绍了 (实在太累了)

在C++中,素数是一个只能被1和自身整除的正整数。也就是说,如果一个数不能被任何其他数整除,则它是素数。
素数在C++中有很多应用,例如:
  1. 判断一个数是否为素数:可以通过循环遍历2到n-1的所有数,判断是否能被整除来判断一个数n是否为素数。

  2. 找出一定范围内的所有素数:可以使用筛选法(筛选法中常用的是埃拉托斯特尼筛选法),从2开始,不断将能被当前选中的数整除的数标记为非素数,直到遍历完所有的数。

  3. 求解素数因子:可以通过不断将一个数n除以素数,直到n不能再被素数整除,最后剩下的n即为最大的素数因子。

  4. 生成大素数:在某些加密算法中,需要生成一个足够大的素数。这可以通过随机生成一个大数,然后使用素数测试算法(如Miller-Rabin算法)来确定该数是否为素数。

总而言之,素数在C++中是一种特殊的数,其只能被1和自身整除。在编程中,我们可以利用素数的特性进行判断、筛选、因子分解等操作。

第二部分 素数筛

素数筛这里简单介绍10种方法,有些有点偏,各位不要介意,具体的应用到题目中详细介绍

1.埃拉托斯特尼素数筛(Sieve of Eratosthenes):这是一种经典的找出一定范围内所有素数的筛选算法。

2.欧拉筛(Sieve of Euler):这种算法是对埃氏筛的改进,可以更高效地找出一定范围内的所有素数。

3.线性筛法(Linear Sieve):这种算法结合了埃氏筛和欧拉筛的优点,可以在O(n)的时间复杂度内找出一定范围内的所有素数。

4.分块筛法(Segmented Sieve):当需要在一个很大范围内找素数时,可以使用分块筛法,分段进行筛选,降低算法的时间复杂度。

5.素数测试算法(Primality Testing):这些算法可以用来判断一个数是否是素数,如费马素性测试、米勒-拉宾素性测试等。

6.质因数分解算法(Prime Factorization):这些算法可以将一个数分解成若干个质因数的乘积,常用的有试除法、分解定理等。

7.素数随机生成算法(Random Prime Generation):这些算法可以随机生成一个指定位数的素数,如Miller-Rabin随机素数生成算法等。

8.快速幂算法(Fast Exponentiation):在进行素数测试或质因数分解时,往往需要进行大数的幂运算,快速幂算法可以提高计算效率。

9.素数间隔问题(Prime Gap Problem):这个问题研究了相邻素数之间的差距,素数筛相关的算法可以用来研究和解决这个问题。

10.利用位运算优化的素数筛算法(Bitwise Optimization):这种算法利用位运算技巧,可以进一步提高素数筛算法的效率。1.埃拉托斯特尼素数筛(Sieve of Eratosthenes):这是一种经典的找出一定范围内所有素数的筛选算法。

一般都是用里面的3 ~ 4种,后面到高阶算法,程序设计优化才会用到 (本蒟蒻不会,望DALAO勿喷)

第三部分:唯一分解定理定义及应用

唯一分解定理(Unique factorization theorem)是数论中的一个重要定理,它指出任何一个整数都可以被唯一地分解为素数(或其负数)的乘积。
具体来说,唯一分解定理可以表述为:任何一个整数n(n ≠ 0,±1)都可以表示为如下形式的乘积:

n = p₁^k₁ * p₂^k₂ * p₃^k₃ * … * pₙ^kₙ
其中,p₁, p₂, p₃, …, pₙ是素数(或其负数),k₁, k₂, k₃, …, kₙ是非负整数。

唯一分解定理的意义在于,它保证了整数的分解是唯一的,即不同的素数分解所得到的结果是不同的。这一定理是数论中的基础定理,它在证明其他定理和问题中起到了重要的作用。
唯一分解定理的应用很广泛。在代数学、密码学和计算机科学等领域中,唯一分解定理是很多关键算法的基础。例如,在密码学中,唯一分解定理被用于RSA加密算法中的公钥和私钥的生成和使用。在代数学中,唯一分解定理被用于研究整数环、多项式环等的性质和结构。在计算机科学中,唯一分解定理被用于数据结构和算法的设计和分析。
总之,唯一分解定理是数论中的一个基本定理,它保证了整数的分解是唯一的,并在许多领域中有广泛的应用。唯一分解定理(Unique factorization theorem)是数论中的一个重要定理,它指出任何一个整数都可以被唯一地分解为素数(或其负数)的乘积。

第四部分:题解

正文来了
本题解主要和素数有关,考察对于素数的认识(我就不解释了,都注释在代码里面)

OJ 网站:东方博宜 网址已放在文章前面了
注意⚠️:东方博宜里面没有相对应的题单,得自己去搜题号!(都是蒟蒻一人搜集过来的……)

题目第一部分:素数

2136 - 筛素数 解法一:简单筛法

//解法一:简单筛法

/*
解释:
定义isPrime函数(bool),用于判断素数
再从1遍历到n,每个数遍历判断
数据量并不大,10^6,不会超限
*/

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

bool isPrime(ll num) {
	//特判:如果 num <= 1 就一定不是素数 
	if (num <= 1) return false;
	for (ll i = 2; i <= sqrt(num); i++) {
		//如果i是num的因数,就代表num不是素数(因为有因数)
		if (num % i == 0) {
			return false;
		}
	}
	return true;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	//ans用于累加素数
	ll n, ans = 0;
	cin >> n;
	//遍历 1~n
	for (ll i = 1; i <= n; i++) {
		if (isPrime(i)) {
			ans++;
		}
	}
	cout << ans << endl;
	return 0;
}

2136 - 筛素数 解法二:埃氏筛(解法一:存数组的方法)

//解法二:埃氏筛
//埃氏筛解法一:存数组的方法

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

//标记素数(用bool数组防止占用内存)
vector<bool> f(1000010);
//存储所有的素数
vector<ll> primes(1000010);
ll n, k;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> n;
	//特判特殊的数
	//不是素数
	f[0] = f[1] = true;

	//循环遍历所有的数,逐个判断
	for (ll i = 2; i <= n; i++) {
		//如果i是素数,i的倍数不是素数
		if (!f[i]) {
			k++;
			primes[k] = i;

			//将i的倍数标记为不是素数
			for (ll j = i * 2; j <= n; j += i) {
				//标记不是素数
				f[j] = true;
			}
		}
	}
	cout << k << endl;
	return 0;
}

2136 - 筛素数 解法三:埃氏筛(解法二:不存数组的方法)

//解法三:埃氏筛
//埃氏筛解法二:不存数组的方法

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

//标记素数(用bool数组防止占用内存)
vector<bool> f(1000010);
//存储所有的素数
//vector<ll> primes(1000010);

//k表示素数的个数
ll n, k;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> n;
	//特判特殊的数
	//不是素数
	f[0] = f[1] = true;

	k = n - 1;	

	//循环遍历所有的数,逐个判断
	//循环到sqrt(n)即可
	for (ll i = 2; i <= sqrt(n); i++) {
		//如果i是素数,i的倍数不是素数
		if (!f[i]) {
//			k++;
//			primes[k] = i;

			//将i的倍数标记为不是素数
			for (ll j = i * 2; j <= n; j += i) {
				//如果没有标记过,则标记
				if (!f[j]) {
					//标记不是素数
					f[j] = true;
					k--;
				}
			}
		}
	}
	cout << k << endl;
	return 0;
}

2136 - 筛素数 解法四:线性筛(欧拉筛)

//解法四:线性筛(欧拉筛)
/*
解释:每个合数只被自己的最小质因子筛除
比如:6在埃氏筛中既能被2筛除,又能被3筛除
我们希望在线性筛中,6仅仅被最小的质因子,也就是2筛除
*/

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

//标记素数(用bool数组防止占用内存)
vector<bool> f(1000010);
//存储所有的素数
vector<ll> primes(1000010);
ll n, k;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	//特判
	f[0] = f[1] = true;
	
	//循环遍历每个数,逐个判断
	for (ll i = 2; i <= n; i++) {
		//如果i是素数,存储
		if (!f[i]) {
			k++;
			primes[k] = i;
		}

		//每个合数,只能被自己的最小值因子筛除
		//j循环的是primes数组
		//筛的是 i * primes[j] 中的数
		for (ll j = 1; i * primes[j] <= n; j++) {
			//将该数筛掉
			f[i * primes[j]] = true;

			//一定要break!
			//如果i % primes[j] == 0
			//i * primes[j + 1] % primes[j] == 0
			if (i % primes[j] == 0) break;
		}
	}
	cout << k << endl;
	return 0;
}

2007 - 半质数 解法一:埃氏筛

/*
半质数:
正整数N,恰好能够分解成两个因数的乘积
如 4 = 2 * 2, 15 = 3 * 5 等,但12不是,因为 12 = 2 * 2 * 3

问:s~e之间有多少个半质数?

思路:
1、先筛出所有的素数
2、两两配对相乘,看能否构建出半质数(在s~e之间)
3、注意两个素数相乘,可能溢出int(用long long)
*/

#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;

vector<bool> f(5000010);
ll s, e, cnt;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> s >> e;
	//筛出2~e范围内的素数
	for (ll i = 2; i <= e; i++) {
		//如果i是素数
		if (!f[i]) {
			//i的倍数不是素数
			for (ll j = i * 2; j <= e; j += i) {
				//标记合数
				f[j] = true;
			}
		}
	}
	//两两素数配对,找出s~e范围的半质数
	for (ll i = 2; i <= sqrt(e); i++) {
		//如果i是素数
		if (!f[i]) {
			//找配对的素数
			for (ll j = i; i * j <= e; j++) {
				if (i * j >= s && !f[j]) {
					cnt++;
				}
			}
		}
	}
	cout << cnt << endl;
	return 0;
}

2007 - 半质数 解法二:线性筛

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;

vector<bool> f(5000010, false);
vector<ll> p;
ll s, e, cnt;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> s >> e;

    if (s < 2) s = 2;  // 半质数定义从2开始

    // 1. 筛素数
    f[0] = f[1] = true;
    for (ll i = 2; i <= e; i++) {
        if (!f[i]) {
            p.push_back(i);
        }
        for (size_t j = 0; j < p.size() && i * p[j] <= e; j++) {
            f[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }

    // 2. 两两配对计算半质数
    cnt = 0;
    for (size_t i = 0; i < p.size(); i++) {
        for (size_t j = i; j < p.size(); j++) {
            ll product = p[i] * p[j];
            if (product > e) break;
            if (product >= s) {
                cnt++;
            }
        }
    }

    cout << cnt << endl;
    return 0;
}

题目第二部分:唯一分解定理

1080 - 质因子

#include <iostream>
using namespace std;
int main()
{
	int n;
	cin >> n;
	int i = 2;//除数
	while (!(n == 1))
	{
		if (n % i == 0)
		{
			n /= i;
			cout << i << endl;
			i = 2;
		}
		else
		{
			i++;
		}
	}
	return 0;
}

2137 - 质因子2

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll n;
	cin >> n;

	//在 2~sqrt(n) 之间找质因子
	for (ll i = 2; i <= sqrt(n); i++) {
		//当i是n的因子时,i是质因子,从n中除掉i
		while (n % i == 0) {
			cout << i << endl;
			n = n / i;
		}
	}

	//如果最后n不是1,n也是质因子
	if (n != 1) cout << n << endl;
	return 0;
}

2140 - 质因子3

本蒟蒻一直没想到用map,导致一直没AC。 是机房大佬告诉我用map的
/*
求质因数及质因数出现的次数:
1、采用map<ll, ll>存储质因数及出现的次数
2、按题意输出规定的格式
*/

#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

//maxn存储最大的质因子
ll n, maxn;
map<ll, ll> m;
map<ll, ll>::iterator it;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;

	//特判n等于1
	if (n == 1) {
		cout << "1=1";
		return 0;
	}

	cout << n << "=";

	/*
	分解质因数
	i * i <= n,因为如果i很大,i * i 会溢出 int
	*/

	for (ll i = 2; i <= sqrt(n); i++) {
		while (n % i == 0) {
			m[i]++;
			n /= i;
			maxn = i;
		}
	}

	//特判
	if (n > 1) {
		m[n]++;
		maxn = max(maxn, n);
	}

	//输出
	for (it = m.begin(); it != m.end(); it++) {
		cout << it->first;
		if (it->second > 1) cout << "^" << it->second;
		//如果不是最后一组输出
		if (it->first != maxn) cout << "*";
	}
	return 0;
}

2138 - 乘积的约数个数

/*
因数个数 = (a1 + 1) * (a2 + 1) * (a3 + 1) * ... * (ai + 1)

1.所有的数相乘,分解质因子,得到质因子的个数
  和每个数单独分解质因子,得到质因子的个数,答案一致

2.两个数相加的结果%10000,和两个数%10000相加,再%10000,结果一致

3.乘法同理(同上)

本题的思路:
将每个数分解质因子,求出每个只因子出现的次数,按公式求解约数个数!
*/

#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

ll n, x, r = 1;
map<ll, ll> m;
map<ll, ll>::iterator it;
const ll M = 1e9 + 7;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (ll i = 1; i <= n; i++) {
		cin >> x;
		//分解x的质因子
		for (ll j = 2; j <= sqrt(x); j++) {
			while (x % j == 0) {
				m[j]++;
				x /= j;
			}
		}
		if (x > 1) m[x]++;
	}

	//按公式计算
	for (it = m.begin(); it != m.end(); it++) {
		r = r * (it->second + 1) % M;
	}
	cout << r << endl;
	return 0;
}

2139 - 乘积的约数和

/*
因数之和 = (p1^0 + p1^1 + ... + p1^a1) * ... * (pi^0 + pi^1 + ... + pi ^ ai)

本题的思路:
将每个数分解质因子,求出每个质因子出现的次数,按公式求解约数个数!
*/

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;

ll n, x, r = 1, t, s;
map<ll, ll> m;
map<ll, ll>::iterator it;
const ll M = 1e9 + 7;
typedef long long ll;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	//读入x并分解质因子
	for (ll i = 1; i <= n; i++) {
		cin >> x;
		//分解
		for (ll j = 2; j <= sqrt(x); j++) {
			while (x % j == 0) {
				m[j]++;
				x /= j;
			}
		}
		if (x > 1) m[x]++;
	}
	
	//计算
	for (it = m.begin(); it != m.end(); it++) {
		//求(pi^0 + pi^1 + ... + pi ^ ai)
		s = 0;
		//t表示 pi ^ ai
		t = 1;
		for (ll i = 0; i <= it->second; i++) {
			s = (s + t) % M;
			t = (t * it->first) % M;
		}
		r = (r * s) % M;
	}
	cout << r << endl;
	return 0;
}

全部AC!

答案正确

完结撒花!

有需求的可以私信哦!
  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值