❤ 作者主页:欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得关注、点赞、收藏、评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉
第一章 质数
一、试除法判定质数
1. 题目描述
给定 n 个正整数 a i a_i ai,判定每个数是否是质数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 a i a_i ai。
输出格式
共 n 行,其中第 i 行输出第 i 个正整数
a
i
a_i
ai 是否为质数,是则输出 Yes
,否则输出 No
。
数据范围
1
≤
n
≤
100
1≤n≤100
1≤n≤100,
1
≤
a
i
≤
2
31
−
1
1≤a_i≤2^{31}−1
1≤ai≤231−1
输入样例:
2
2
6
输出样例:
Yes
No
2. 思路分析
质数定义: 一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数,这个数就是质数。
给定一个数
x
x
x,判断
x
x
x 是否为质数:
用
x
x
x 除以
[
2
,
x
−
1
]
[2 ,x - 1]
[2,x−1] 中的每个数,如果出现了余数为 0, 则这个数不是质数,如果没有出现余数为 0,则这个数是质数。
优化:
一个数
x
x
x 分解成两个数的乘积,则这两个数中,一定有一个数大于
x
\sqrt{x}
x,一个数小于
x
\sqrt{x}
x。
所以,可以用
x
x
x 除以 [2,
x
\sqrt{x}
x] 中的每个数,如果出现了余数为 0,则这个数不是质数,如果没有出现余数为 0,则这个数是质数。
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
bool is_prime(int x)
{
if (x < 2) return false; //2是最小的质数,如果n小于2,那n肯定就不是质数
for (int i = 2; i <= x / i; i ++) //不要用开方函数或者i*i小于x。开方函数慢,i*i可能越界
if (x % i == 0)
return false;
return true;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int x;
cin >> x;
if (is_prime(x)) puts("Yes");
else puts("No");
}
return 0;
}
二、分解质因数
1. 题目描述
给定 n 个正整数 a i a_i ai,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 a i a_i ai。
输出格式
对于每个正整数 a i a_i ai,按照从小到大的顺序输出其分解质因数后,每个质因数的底数和指数,每个底数和指数占一行。
每个正整数的质因数全部输出完毕后,输出一个空行。
数据范围
1
≤
n
≤
100
1≤n≤100
1≤n≤100,
2
≤
a
i
≤
2
×
1
0
9
2≤a_i≤2×10^9
2≤ai≤2×109
输入样例:
2
6
8
输出样例:
2 1
3 1
2 3
2. 思路分析
- x x x 的质因子最多只包含一个大于 x \sqrt{x} x 的质数。如果有两个,这两个因子的乘积就会大于 x x x,矛盾。
- i i i 从 2 遍历到 x \sqrt{x} x。 用 x / i x / i x/i,如果余数为 0,则 i i i 是一个质因子。
- s s s 表示质因子 i i i 的指数, x / = i x /= i x/=i 为 0,则 s + + s++ s++, x = x / i x = x / i x=x/i 。
- 最后检查是否有大于 x \sqrt{x} x 的质因子,如果有,输出。
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
void divide(int x)
{
for (int i = 2; i <= x / i; i ++) //i <= x / i:防止越界,速度大于 i < sqrt(x)
if (x % i == 0) //i为底数
{
int s = 0; //s为指数
while (x % i == 0) x /= i, s ++;
cout << i << ' ' << s << endl; //输出底数和指数
}
if (x > 1) cout << x << ' ' << "1" << endl; //如果x还有剩余,单独处理
cout << endl;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int x;
cin >> x;
divide(x);
}
return 0;
}
三、筛质数
1.题目描述
给定一个正整数 n,请你求出 1 ∼ n 1∼n 1∼n 中质数的个数。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示 1 ∼ n 1∼n 1∼n 中质数的个数。
数据范围
1 ≤ n ≤ 1 0 6 1≤n≤10^6 1≤n≤106
输入样例:
8
输出样例:
4
2. 思路分析
线性筛法(又称欧拉筛法)
从小到大每个每个数:
- 如果当前数没被划掉,必定是质数,将该质数加入数组;
- 枚举已记录的质数(如果合数已越界则中断)
(1)合数未越界,则划掉合数;
(2)条件i % p == 0
,保证合数只被最小质因子划掉- 若 i i i 是质数,则最多枚举到自身中断;
- 若 i i i 是合数,则最多枚举到自身的最小质数中断。
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
//primes数组用来存放质数,cnt保存质数的个数
int primes[N], cnt;
bool st[N]; //st[i],i为质数则为false,否则为true
void get_primes(int n)
{
for (int i = 2; i <= n; i ++)
{
if (!st[i]) primes[cnt ++] = i; //当前数是质数,加入数组
for (int j = 0; primes[j] <= n / i; j ++)
{
//标记,primes[j]一定是primes[j]*i的最小质因子
st[primes[j] * i] = true;
//表明primes[j]一定是i的最小质因子,没有必要再遍历,primes要小于等于i的最小质因子
//这样能保证每个数遍历一遍,而没有重复
//每个数只被它的最小质因数消除
if (i % primes[j] == 0) break;
}
}
}
int main()
{
int n;
cin >> n;
get_primes(n);
cout << cnt << endl;
return 0;
}
第二章 约数
一、试除法求约数
1. 题目描述
给定 n 个正整数 a i a_i ai,对于每个整数 a i a_i ai,请你按照从小到大的顺序输出它的所有约数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 a i a_i ai。
输出格式
输出共 n 行,其中第 i 行输出第 i 个整数 a i a_i ai 的所有约数。
数据范围
1
≤
n
≤
100
1≤n≤100
1≤n≤100,
2
≤
a
i
≤
2
×
1
0
9
2≤ a_i≤2×10^9
2≤ai≤2×109
输入样例:
2
6
8
输出样例:
1 2 3 6
1 2 4 8
2. 思路分析
与试除法判定质数一样,约数都是成对出现,故只需判定约数中的较小数即可。
因此,只需要用
x
x
x 除以 1 到 根号x 之间的数,如果余数是0,则把 除数
以及 x / 除数
加到答案中。
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
vector<int> get_divisor(int x)
{
vector<int> res;
//因为约数成对出现,所以只需要循环到根号x
for (int i = 1; i <= x / i; i ++)
if (x % i == 0)
{
res.push_back(i);
//如果n不是i的平方,那n / i就是一个新的约数,把n / i压入vector
if (i != x / i) res.push_back(x / i);
}
sort(res.begin(), res.end());
return res;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int x;
cin >> x;
auto res = get_divisor(x);
for (auto x : res) cout << x << ' ';
cout << endl;
}
return 0;
}
二、约数个数
1. 题目描述
给定 n 个正整数 a i a_i ai,请你输出这些数的乘积的约数个数,答案对 1 0 9 + 7 10^9+7 109+7 取模。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 a i a_i ai。
输出格式
输出一个整数,表示所给正整数的乘积的约数个数,答案需对 1 0 9 + 7 10^9+7 109+7 取模。
数据范围
1
≤
n
≤
100
1≤n≤100
1≤n≤100,
1
≤
a
i
≤
2
×
1
0
9
1≤a_i≤2×10^9
1≤ai≤2×109
输入样例:
3
2
6
8
输出样例:
12
2. 思路分析
N = ∏ i = 1 k p i a i = p 1 a 1 ⋅ p 2 a 2 ⋅ ⋅ ⋅ p k a k N = \displaystyle \prod_{i=1}^{k}{p_i^{a_i}} = p_1^{a_1} · p_2^{a_2} ···p_k^{a_k} N=i=1∏kpiai=p1a1⋅p2a2⋅⋅⋅pkak
约数个数: ∏ i = 1 k ( a i + 1 ) = ( a 1 + 1 ) ( a 2 + 1 ) ⋅ ⋅ ⋅ ( a k + 1 ) \displaystyle \prod_{i=1}^{k}{(a_i+1)} = (a_1+1)(a_2+1)···(a_k+1) i=1∏k(ai+1)=(a1+1)(a2+1)⋅⋅⋅(ak+1)
约数之和:
∏
i
=
1
k
∑
j
=
0
a
i
p
i
j
=
\displaystyle \prod_{i=1}^{k}{\displaystyle \sum_{j=0}^{a_i}p_i^j} =
i=1∏kj=0∑aipij=
∏
i
=
1
k
(
p
i
0
+
p
i
1
+
.
.
.
+
p
i
a
i
)
\displaystyle \prod_{i=1}^{k}{(p_i^0+p_i^1+...+p_i^{a_i})}
i=1∏k(pi0+pi1+...+piai) =
(
p
1
0
+
p
1
1
+
.
.
.
.
+
p
1
a
1
)
(
p
2
0
+
p
3
1
+
.
.
.
.
+
p
2
a
2
)
⋅
⋅
⋅
(
p
k
0
+
p
k
1
+
.
.
.
.
+
p
k
a
k
)
(p_1^0 + p_1^1 + ....+p_1^{a_1})(p_2^0 + p_3^1 + ....+p_2^{a_2})···(p_k^0 + p_k^1 + ....+p_k^{a_k})
(p10+p11+....+p1a1)(p20+p31+....+p2a2)⋅⋅⋅(pk0+pk1+....+pkak)
思路就是先把原数分解为质因数,最后把每一个数的指数累加即可。从a1一直分解到an,由于a的数据过大,此处用哈希表进行存储。
3. 代码实现
#include <bits/stdc++.h>
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] ++; //指数+1
}
//如果有剩余,也是一个质因子
if (x > 1) primes[x] ++;
}
LL res = 1;
//res = (x^1 + 1)(x^2 + 1)(x^3 + 1)…(x^k + 1)
for (auto p : primes) res = res * (p.second + 1) % mod;
cout << res << endl;
return 0;
}
三、约数之和
1.题目描述
给定 n 个正整数 a i a_i ai,请你输出这些数的乘积的约数之和,答案对 1 0 9 + 7 10^9+7 109+7 取模。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 a i a_i ai。
输出格式
输出一个整数,表示所给正整数的乘积的约数之和,答案需对 1 0 9 + 7 10^9+7 109+7 取模。
数据范围
$1≤n≤100,
1
≤
a
i
≤
2
×
1
0
9
1≤a_i≤2×10^9
1≤ai≤2×109
输入样例:
3
2
6
8
输出样例:
252
2. 思路分析
N = ∏ i = 1 k p i a i = p 1 a 1 ⋅ p 2 a 2 ⋅ ⋅ ⋅ p k a k N = \displaystyle \prod_{i=1}^{k}{p_i^{a_i}} = p_1^{a_1} · p_2^{a_2} ···p_k^{a_k} N=i=1∏kpiai=p1a1⋅p2a2⋅⋅⋅pkak
约数个数: ∏ i = 1 k ( a i + 1 ) = ( a 1 + 1 ) ( a 2 + 1 ) ⋅ ⋅ ⋅ ( a k + 1 ) \displaystyle \prod_{i=1}^{k}{(a_i+1)} = (a_1+1)(a_2+1)···(a_k+1) i=1∏k(ai+1)=(a1+1)(a2+1)⋅⋅⋅(ak+1)
约数之和:
∏
i
=
1
k
∑
j
=
0
a
i
p
i
j
=
\displaystyle \prod_{i=1}^{k}{\displaystyle \sum_{j=0}^{a_i}p_i^j} =
i=1∏kj=0∑aipij=
∏
i
=
1
k
(
p
i
0
+
p
i
1
+
.
.
.
+
p
i
a
i
)
\displaystyle \prod_{i=1}^{k}{(p_i^0+p_i^1+...+p_i^{a_i})}
i=1∏k(pi0+pi1+...+piai) =
(
p
1
0
+
p
1
1
+
.
.
.
.
+
p
1
a
1
)
(
p
2
0
+
p
3
1
+
.
.
.
.
+
p
2
a
2
)
⋅
⋅
⋅
(
p
k
0
+
p
k
1
+
.
.
.
.
+
p
k
a
k
)
(p_1^0 + p_1^1 + ....+p_1^{a_1})(p_2^0 + p_3^1 + ....+p_2^{a_2})···(p_k^0 + p_k^1 + ....+p_k^{a_k})
(p10+p11+....+p1a1)(p20+p31+....+p2a2)⋅⋅⋅(pk0+pk1+....+pkak)
- 在每次输入一个数时,分解其质因数,将其出现的次数保存起来;
- 遍历保存质因数的表,将每个质因数带入公式中。
3. 代码实现
#include <bits/stdc++.h>
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;
res = res * t % mod;
}
cout << res << endl;
return 0;
}
四、最大公约数
1. 题目描述
给定 n 对正整数 a i , b i a_i,b_i ai,bi,请你求出每对数的最大公约数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数对 a i , b i a_i,b_i ai,bi。
输出格式
输出共 n 行,每行输出一个整数对的最大公约数。
数据范围
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105,
1
≤
a
i
,
b
i
≤
2
×
1
0
9
1≤a_i,b_i≤2×10^9
1≤ai,bi≤2×109
输入样例:
2
3 6
4 6
输出样例:
3
2
2. 思路分析
欧几里得算法(辗转相除法)
(
a
,
b
)
(a, b)
(a,b) 和 (b, a%b) 的公约数是相同的,故最大公约数也相同,即 gcd(a, b) = gcd(b, a % b)
递归求解:直到 a % b == 0
,此时的
b
b
b 就是最大公约数。
模拟:求28和16的最大公约数
gcd(28,16)
gcd(16,12)
gcd(4,0)
return 4
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int a, b;
cin >> a >> b;
cout << gcd(a, b) << endl;
}
return 0;
}
第三章 快速幂
一、快速幂
1. 题目描述
给定 n 组 a i , b i , p i a_i,b_i,p_i ai,bi,pi,对于每组数据,求出 a i b i m o d p i a_i^{b_i} mod p_i aibimodpi 的值。
输入格式
第一行包含整数 n。
接下来 nn 行,每行包含三个整数 a i , b i , p i a_i,b_i,p_i ai,bi,pi。
输出格式
对于每组数据,输出一个结果,表示 a i b i m o d p i a_i^{b_i} mod p_i aibimodpi 的值。
每个结果占一行。
数据范围
1
≤
n
≤
100000
1≤n≤100000
1≤n≤100000,
1
≤
a
i
,
b
i
,
p
i
≤
2
×
1
0
9
1≤a_i,b_i,p_i≤2×10^9
1≤ai,bi,pi≤2×109
输入样例:
2
3 2 5
4 3 9
2. 思路分析
a
n
=
a
×
a
×
⋅
⋅
⋅
×
a
a^n = a×a×···×a
an=a×a×⋅⋅⋅×a,暴力的计算需要
O
(
n
)
O(n)
O(n)的时间。
快速幂使用二进制拆分和倍增思想,仅需要
O
(
l
o
g
n
)
O(logn)
O(logn) 的时间。
对
n
n
n 做二进制拆分,例如:
3
13
=
3
(
1101
)
2
=
3
8
⋅
3
4
⋅
3
1
3^{13} = 3^{(1101)_2} = 3^8 · 3^4 ·3^1
313=3(1101)2=38⋅34⋅31
对
a
a
a 做平方倍增,例如:
3
1
,
3
2
,
3
4
,
3
8
,
.
.
.
.
.
3^1,3^2,3^4,3^8,.....
31,32,34,38,.....
n n n 有 l o g n + 1 logn + 1 logn+1 个二进制,我知道了 3 1 , 3 2 , 3 4 , 3 8 , . . . . . , a 2 l o g n 3^1,3^2,3^4,3^8,.....,a^{2^{logn}} 31,32,34,38,.....,a2logn后,只需要计算 l o g n + 1 logn + 1 logn+1 次乘法就可以了。
模拟求出 3 13 3^{13} 313:
- (1101) & 1 = 1; res = 1 * 3 = 3; a = 3 * 3 = 3 2 3^2 32; n = (110);
- (110) & 1 = 0; a = 3 2 3^2 32 * 3 2 3^2 32 = 3 4 3^4 34; n = (11);
- (11) & 1 = 1; res = 3 * 3 4 3^4 34 ; a = 3 4 3^4 34 * 3 4 3^4 34 = 3 8 3^8 38; n = (1);
- (1) & 1 = 1; res = 3 * 3 4 3^4 34 * 3 8 3^8 38 ; a = 3 8 3^8 38 * 3 8 3^8 38 = 3 16 3^{16} 316; n = (0);
- 返回 r e s res res。
快速幂可以应用在任何具有结合律的运算中,例如:取模运算、矩阵乘法等。
例如:
(
3
13
)
(3^{13})
(313) %
p
p
p = (
(
3
8
)
(3^8)
(38) %
p
p
p ·
(
3
4
)
(3^4)
(34) %
p
p
p ·
(
3
1
)
(3^1)
(31) %
p
p
p) %
p
p
p
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
// 求k的二进制数,再用这二进制数求出a的k次方模p的结果
while (b)
{
// 如果当前k的末尾是1
if (b & 1) res = res * a % p;
// 置a的值为a的平方模p
a = a * (LL)a % p;
//向右移一位
b >>= 1;
}
return res;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int a, b, p;
cin >> a >> b >> p;
cout << qmi(a, b, p) << endl;
}
return 0;
}
二、快速幂求逆元
1. 题目描述
给定 n 组
a
i
,
p
i
a_i,p_i
ai,pi,其中
p
i
p_i
pi 是质数,求
a
i
a_i
ai 模
p
i
p_i
pi 的乘法逆元,若逆元不存在则输出 impossible
。
注意:请返回在 0 ∼ p − 1 0∼p−1 0∼p−1 之间的逆元。
乘法逆元的定义
若整数 b,m 互质,并且对于任意的整数 a,如果满足 b|a,则存在一个整数 x,使得 a/b≡a×x(modm),则称 x 为 b 的模 m 乘法逆元,记为 b − 1 b^{−1} b−1(modm)。
b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时, b m − 1 b^{m-1} bm−1 即为 bb 的乘法逆元。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个数组 a i , p i a_i,p_i ai,pi,数据保证 p i p_i pi 是质数。
输出格式
输出共 n 行,每组数据输出一个结果,每个结果占一行。
若
a
i
a_i
ai 模
p
i
p_i
pi 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible
。
数据范围
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105,
1
≤
a
i
,
p
i
≤
2
∗
1
0
9
1≤a_i,p_i≤2∗10^9
1≤ai,pi≤2∗109
输入样例:
3
4 3
8 5
6 3
输出样例:
1
2
impossible
2. 思路分析
3. 代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1;
while (b)
{
if (b & 1) res = res * a % p;
a = a * (LL)a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
cin >> n;
while (n --)
{
int a, p;
cin >> a >> p;
if (a % p == 0) puts("impossible");
else cout << qmi(a, p - 2, p) << endl;
}
return 0;
}
创作不易,如果有帮助到你,请给文章点个赞和收藏,让更多的人看到!!!
关注博主不迷路,内容持续更新中。