啊啊啊,好久没发过题解啦,望各位大佬 RP++
食用OJ : 东方博宜
网站:东方博宜
第一部分:素数
这里想必大家都很了解了,就不详细介绍了 (实在太累了)
在C++中,素数是一个只能被1和自身整除的正整数。也就是说,如果一个数不能被任何其他数整除,则它是素数。
素数在C++中有很多应用,例如:
-
判断一个数是否为素数:可以通过循环遍历2到n-1的所有数,判断是否能被整除来判断一个数n是否为素数。
-
找出一定范围内的所有素数:可以使用筛选法(筛选法中常用的是埃拉托斯特尼筛选法),从2开始,不断将能被当前选中的数整除的数标记为非素数,直到遍历完所有的数。
-
求解素数因子:可以通过不断将一个数n除以素数,直到n不能再被素数整除,最后剩下的n即为最大的素数因子。
-
生成大素数:在某些加密算法中,需要生成一个足够大的素数。这可以通过随机生成一个大数,然后使用素数测试算法(如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;
}