整理了几道素数筛法相关的几道练习题
[蓝桥杯 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
样例输入 #1
12
样例输出 #1
3
样例 #2
样例输入 #2
15
样例输出 #2
15
提示
对于 30 % 30 \% 30% 的评测用例, 1 ≤ n ≤ 1000 1 \leq n \leq 1000 1≤n≤1000,答案不超过 1000 1000 1000。
对于 60 % 60 \% 60% 的评测用例, 1 ≤ n ≤ 1 0 8 1 \leq n \leq 10^{8} 1≤n≤108,答案不超过 1 0 8 10^{8} 108。
对于所有评测用例, 1 ≤ n ≤ 1 0 12 1 \leq n \leq 10^{12} 1≤n≤1012,答案不超过 1 0 12 10^{12} 1012。
蓝桥杯 2021 第二轮省赛 A 组 G 题(B 组 H 题)。
#include <bits/stdc++.h>
using namespace std;
/*
给我这个数学不好的菜狗一点小小的数论震撼
完全平方数如果进行因数分解的话
所有因子都是有偶数个的
对n进行质因数分解
若质因子指数为偶数
则对结果无影响
若质因子指数为奇数
则在x中乘以这个质因子
保证指数为偶数
另外我这题爆int了
然后for循环判断条件就乱了
评测机显示TLE导致我de不出来
*/
long long n;
long long ans = 1;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> 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) ans *= i;
}
}
//要注意n未除完的情况
if(n != 1) ans *= n;
cout << ans;
return 0;
}
哥德巴赫猜想(升级版)
题目背景
1742 年 6 月 7 日,哥德巴赫写信给当时的大数学家欧拉,正式提出了以下的猜想:任何一个大于 9 9 9 的奇数都可以表示成 3 3 3 个质数之和。质数是指除了 1 1 1 和本身之外没有其他约数的数,如 2 2 2 和 11 11 11 都是质数,而 6 6 6 不是质数,因为 6 6 6 除了约数 1 1 1 和 6 6 6 之外还有约数 2 2 2 和 3 3 3。需要特别说明的是 1 1 1 不是质数。
这就是哥德巴赫猜想。欧拉在回信中说,他相信这个猜想是正确的,但他不能证明。
从此,这道数学难题引起了几乎所有数学家的注意。哥德巴赫猜想由此成为数学皇冠上一颗可望不可及的“明珠”。
题目描述
现在请你编一个程序验证哥德巴赫猜想。
先给出一个奇数 n n n,要求输出 3 3 3 个质数,这 3 3 3 个质数之和等于输入的奇数。
输入格式
仅有一行,包含一个正奇数 n n n,其中 9 < n < 20000 9 < n < 20000 9<n<20000。
输出格式
仅有一行,输出 3 3 3 个质数,这 3 3 3 个质数之和等于输入的奇数。相邻两个质数之间用一个空格隔开,最后一个质数后面没有空格。如果表示方法不唯一,请输出第一个质数最小的方案,如果第一个质数最小的方案不唯一,请输出第一个质数最小的同时,第二个质数最小的方案。
样例 #1
样例输入 #1
2009
样例输出 #1
3 3 2003
#include <bits/stdc++.h>
using namespace std;
/*
先筛出范围内所有的质数
再对质数进行深搜
水。。。
*/
const int N = 20005;
int n;
bool st[N];
int primes[N];
int cnt;
int path[4];
bool flag;
inline void get_primes()
{ //先筛出数据范围内所有质数
for(int i = 2; i <= N; i ++) {
if(!st[i]) primes[++ cnt] = i;
for(int j = 1; primes[j] * i <= N; j ++) {
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
inline void dfs(int d, int r, int sum)
{ //d为搜索深度,r为质数次序,sum为当前总和
if(flag) return;
if(d > 3 && sum == n) {
int t = 0;
for(int i = 1; i <= 3; i ++) {
cout << path[i];
if(i != 3) cout << ' ';
}
flag = 1;
return;
}
else if(d > 3 && sum != n) return;
for(int i = r; i <= cnt; i ++) {
if(sum + primes[i] <= n) {
path[d] = primes[i];
dfs(d + 1, i, sum + primes[i]);
path[d] = 0;
}
else break;
}
}
int main()
{
cin >> n;
get_primes();
dfs(1, 1, 0);
return 0;
}
斐波那契数列(升级版)
题目背景
大家都知道,斐波那契数列是满足如下性质的一个数列:
- f ( 1 ) = 1 f(1) = 1 f(1)=1
- f ( 2 ) = 1 f(2) = 1 f(2)=1
- f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n-1) + f(n-2) f(n)=f(n−1)+f(n−2)( n > 2 n > 2 n>2 且 n n n 为整数)。
题目描述
请你求出第 n n n 个斐波那契数列的数 m o d 2 31 \bmod\,2^{31} mod231 之后的值,并把它分解质因数。
输入格式
输入一个正整数 n n n。
输出格式
把第 n n n 个斐波那契数列的数分解质因数。
样例 #1
样例输入 #1
5
样例输出 #1
5=5
样例 #2
样例输入 #2
6
样例输出 #2
8=2*2*2
提示
n ≤ 48 n \le 48 n≤48
感谢我的高中同学ht 不然我可能一直会用递归来求斐波那契
但实际上用for循环遍历一遍即可
#include <bits/stdc++.h>
using namespace std;
/*
用dfs来求斐波那契太慢了
是真的慢炸了我以为IDE坏了。。
改成for循环求斐波那契
*/
typedef long long LL;
const LL mod = pow(2, 31);
int n;
LL num[50];
queue<int> q;
LL get_num(int x)
{
for(int i = 1; i <= x; i ++) {
if(i == 1 || i == 2) num[i] = 1;
else num[i] = num[i - 1] + num[i - 2];
}
return num[x];
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
LL t = get_num(n) % mod;
cout << t << '=';
for(LL i = 2; i * i <= t; i ++) {
while(t % i == 0) t /= i, q.push(i);
}
if(t != 1) q.push(t);
cout << q.front();
q.pop();
while(q.size()) {
cout << '*' << q.front();
q.pop();
}
return 0;
}
A+B Problem(再升级)
题目背景
题目名称是吸引你点进来的。
实际上该题还是很水的。
题目描述
- 1 + 1 = ? 1+1=? 1+1=? 显然是 2 2 2。
- a + b = ? a+b=? a+b=? P1001 回看不谢。
- 哥德巴赫猜想 似乎已呈泛滥趋势。
以上纯属个人吐槽
给定一个正整数 n n n,求将其分解成若干个素数之和的方案总数。
输入格式
一行一个正整数 n n n。
输出格式
一行一个整数表示方案总数。
样例 #1
样例输入 #1
7
样例输出 #1
3
样例 #2
样例输入 #2
20
样例输出 #2
26
提示
样例解释
存在如下三种方案:
- 7 = 7 7=7 7=7。
- 7 = 2 + 5 7=2+5 7=2+5。
- 7 = 2 + 2 + 3 7=2+2+3 7=2+2+3。
数据范围及约定
- 对于 30 % 30\% 30% 的数据 1 ≤ n ≤ 10 1\le n\le 10 1≤n≤10。
- 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 3 1\le n\le 10^3 1≤n≤103。
ycy同学,看到这里的时候请你去复习一下完全背包!
#include <bits/stdc++.h>
using namespace std;
/*
筛完质数
裸背包
爱切水题
补一句
是完全背包
然后我完全背包板子写错了
果然太久不做就忘光了
*/
typedef long long LL;
const int N = 1005;
int n;
LL f[N]; //f[i]为总和为 i 的方案数
int cnt, primes[N];
bool st[N];
void get_primes()
{
for(int i = 2; i <= N; i ++) {
if(!st[i]) primes[++ cnt] = i;
for(int j = 1; primes[j] * i <= N; j ++) {
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
get_primes();
f[0] = 1;
for(int i = 1; i <= cnt; i ++) {
for(int j = primes[i]; j <= n; j ++) {
f[j] += f[j - primes[i]];
}
}
cout << f[n];
return 0;
}
[AHOI2001]质数和分解
题目描述
任何大于 1 1 1 的自然数 n n n 都可以写成若干个大于等于 2 2 2 且小于等于 n n n 的质数之和表达式(包括只有一个数构成的和表达式的情况),并且可能有不止一种质数和的形式。例如, 9 9 9 的质数和表达式就有四种本质不同的形式:
9 = 2 + 5 + 2 = 2 + 3 + 2 + 2 = 3 + 3 + 3 = 2 + 7 9 = 2 + 5 + 2 = 2 + 3 + 2 + 2 = 3 + 3 + 3 = 2 + 7 9=2+5+2=2+3+2+2=3+3+3=2+7 。
这里所谓两个本质相同的表达式是指可以通过交换其中一个表达式中参加和运算的各个数的位置而直接得到另一个表达式。
试编程求解自然数 n n n 可以写成多少种本质不同的质数和表达式。
输入格式
文件中的每一行存放一个自然数 n ( 2 ≤ n ≤ 200 ) n(2 \leq n \leq 200) n(2≤n≤200) 。
输出格式
依次输出每一个自然数 n n n 的本质不同的质数和表达式的数目。
样例 #1
样例输入 #1
2
200
样例输出 #1
1
9845164
#include <bits/stdc++.h>
using namespace std;
/*
这升级版也不太行嘛!
不就是求一次改成了求好多次吗
*/
typedef long long LL;
const int N = 205;
int n;
LL f[N]; //f[i]为总和为 i 的方案数
int cnt, primes[N];
bool st[N];
void get_primes()
{
for(int i = 2; i <= N; i ++) {
if(!st[i]) primes[++ cnt] = i;
for(int j = 1; primes[j] * i <= N; j ++) {
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
LL amount(int n)
{
memset(f, 0, sizeof f);
f[0] = 1;
for(int i = 1; i <= cnt; i ++) {
for(int j = primes[i]; j <= n; j ++) {
f[j] += f[j - primes[i]];
}
}
return f[n];
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
get_primes();
while(cin >> n)
{
cout << amount(n) << endl;
}
return 0;
}
Divided Prime
题目描述
给定一个数字 A A A,这个 A A A由 a 1 , a 2 , ⋯ , a N a_1,a_2,\cdots,a_N a1,a2,⋯,aN相乘得到。
给定一个数字 B B B,这个 B B B由 b 1 , b 2 , ⋯ , b M b_1,b_2,\cdots,b_M b1,b2,⋯,bM相乘得到。
如果
A
B
\frac{A}{B}
BA是一个质数,请输出YES
,否则输出NO
。
输入格式
每个测试点包含多组数据,第一行读入一个整数 T T T 表示数据组数,对于每组数据:
第一行输入两个整数 N , M N,M N,M,分别表示 A A A 由 N N N 个数字相乘得到, B B B 由 M M M 个数字相乘得到。
第二行输入 N N N 个整数,分别表示组成 A A A 的 N N N 个数字。
第三行输入 M M M 个整数,分别表示组成 B B B 的 M M M 个数字。
保证对于一个数字,其在 b i {b_i} bi 中出现的次数不多于在 a i {a_i} ai 中出现的次数。
输出格式
对于每组数据:
如果
A
B
\frac{A}{B}
BA 是一个质数,请输出 YES
,否则输出 NO
。
在输出 YES
或 NO
后输出一个换行符。
样例 #1
样例输入 #1
2
3 2
5 7 7
5 7
4 2
5 7 7 7
5 7
样例输出 #1
YES
NO
提示
1 ≤ N ≤ 100000 1 \le N \le 100000 1≤N≤100000
0 ≤ M ≤ N 0 \le M \le N 0≤M≤N
1 ≤ a i , b i ≤ 1 0 12 1 \le a_i,b_i \le 10^{12} 1≤ai,bi≤1012
1 ≤ T ≤ 10 1 \le T \le 10 1≤T≤10
∑ N ≤ 100000 \sum N \le 100000 ∑N≤100000
#include <bits/stdc++.h>
using namespace std;
/*
由题知
在分母中出现的数
一定在分子中出现过
而且分子中出现数量更多
所以只要把分子的数记录下来
再根据分母进行删除
最后乘起来判断即可
不过这种方法太暴力
效率不够好
其实在约掉一些数之后
剩下的数字
如果数量仍在2个以上
则肯定会形成合数
所以最多保留一个数字
然后再判断留下的这个数字是不是质数
要记住1不是质数!
*/
typedef long long LL;
int T, N, M;
bool check(vector<int> v, map<int, int> mp)
{//要注意1不是质数
vector<int> t;
for(int i = 0; i < v.size(); i ++) {
if(mp[v[i]] >= 2) return false;
else if(mp[v[i]] == 1 && v[i] != 1) t.push_back(v[i]);
}
if(t.size() != 1) return false;
else {
for(int i = 2; i <= t[0] / 2; i ++) {
if(t[0] % i == 0) return false;
}
return true;
}
}
int main()
{
cin >> T;
while(T --) {
vector<int> v;
map<int, int> mp;
cin >> N >> M;
for(int i = 1; i <= N; i ++) {
LL a;
cin >> a;
if(mp.count(a)) {
mp[a] ++;
}
else {
mp[a] = 1;
v.push_back(a);
}
}
for(int i = 1; i <= M; i ++) {
LL a;
cin >> a;
mp[a] --;
}
if(check(v, mp)) cout << "YES";
else cout << "NO";
if(T) cout << '\n';
}
return 0;
}