素数筛
Almost Prime
#include <bits/stdc++.h>
using namespace std;
const int N = 3000;
vector<bool> is_prime(N+1, true);
vector<int> primes;
// 埃氏筛
void sieve() {
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= N; i++) {
if (is_prime[i]) {
primes.push_back(i);
for (int j = 2*i; j <= N; j += i) {
is_prime[j] = false;
}
}
}
}
int count(int n) {
sieve(); // 初始化质数表
int countt = 0; // 几乎质数的计数
for (int i = 1; i <= n; i++) {
int temp = i;
int discount = 0; // 当前数的不同质因子计数,需要在循环内重置
for (int j = 0; j < primes.size() && primes[j] * primes[j] <= temp; ++j) {
if (temp % primes[j] == 0) {
discount++;
while (temp % primes[j] == 0) temp /= primes[j]; // 移除所有相同的质因子
}
}
if (temp > 1) discount++; // 处理剩余的质因子
if (discount == 2) countt++; // 如果恰好有两个不同的质因子
}
return countt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
cout << count(n) << endl;
}
if (temp > 1) distinctPrimeFactors++; // 处理剩余的大于sqrt(i)的质因子
这句代码的作用是处理那些在遍历质数并尝试除以它们时剩余的、未被完全除尽的质因子。具体来说,在检查一个数i
的质因子时,通常的做法是遍历小于等于sqrt(i)
的所有质数,因为一个合数必有一个不大于它的平方根的质因子。
在这个过程中,对于每一个遍历到的质数p
,如果i
可以被p
整除,我们就把i
除以p
,直到i
不能再被p
整除。这样做的目的是找出i
的所有质因子,并计算它有多少个不同的质因子。
但是,在遍历完所有小于等于sqrt(i)
的质数后,可能还剩下一个大于sqrt(i)
的质因子。例如,假设i = 2 * 7 * 13
,当我们遍历到7并除以7之后,剩下的数是13,它大于sqrt(i)
。在这种情况下,我们需要增加distinctPrimeFactors
的计数,以确保计算的质因子数量是正确的。
这句话的逻辑是:
- 如果在完成上述遍历后
temp
大于1,这意味着i
除以它的所有小于等于sqrt(i)
的质因子后,还剩下一个大于sqrt(i)
的质因子。这个剩余的temp
本身就是一个质因子,因为如果它是合数,它就会在之前的遍历中被除尽。 - 因此,我们需要把这个剩余的质因子计入总的不同质因子数量中,即通过
distinctPrimeFactors++
来实现。
这个步骤对于确保当一个数具有大于sqrt(i)
的质因子时能够正确计算其质因子总数是必需的。
[ARC017A] 素数、コンテスト、素数
//日本题目记得答案要输出换行
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
vector<bool> is_primes(N+1,true);
vector<int> primes;
void linesieve(){
is_primes[0] = is_primes[1] = false;
for(int i=2; i<=N; i++){
if(is_primes[i]){
primes.push_back(i);
}
for(int j=0; j < primes.size() && i * primes[j] <= N; ++j){
is_primes[i * primes[j]] = false;
if(i % primes[j] == 0) break;
}
}
}
bool isprime(int n){
if(n<=1) return false;
for(int i=2; i * i <=n; i++){
if(n % i == 0){
return false;
}
}
return true;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
linesieve();
if(is_primes[n]){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
[ARC032A] ホリドッグ
#include <bits/stdc++.h>
using namespace std;
bool is_prime(int n){
if(n<2) return false;
for(int i=2; i*i<=n; i++){
if(n % i == 0){
return false;
}
}
return true;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
int sum = 0 ;
for(int i=1; i<=n; i++){
sum += i;
}
if(is_prime(sum)){
cout<<"WANWAN"<<endl;
}else{
cout<<"BOWWOW"<<endl;
}
}
与えられた数より小さい素数の個数について
与えられた数より小さい素数の個数について
题目要求找出自然数
n
n
n 之下(不包含
n
n
n 本身)的所有素数的个数。素数是指只有1和自身两个正因子的自然数,比如2、3、5、7等。这个问题可以通过素数筛法来高效解决,特别是使用埃拉托斯特尼筛法(Eratosthenes筛法)或线性筛法。
思路
-
初始化:创建一个布尔数组
is_prime
,长度为 n n n,用于标记每个数字是否为素数,初始化时假设所有数字都是素数(除了0和1,它们不是素数)。 -
筛选素数:遍历从2开始到 n − 1 n-1 n−1 的每一个数字,对于每一个数字 i i i,如果它被标记为素数(
is_prime[i]
为true
),则将所有 i i i 的倍数(不包括 i i i 自己,从 i 2 i^2 i2 开始更高效)标记为非素数(不是素数的意思)。 -
线性筛的优化:在筛选过程中,为了减少重复标记的操作,当一个数 i i i 被它最小的素因数筛除时,就不再继续筛选了。这是因为对于任意合数 x = i × p x = i \times p x=i×p(其中 p ≤ i p \leq i p≤i 且 p p p 为素数),在之前它已经被 p p p 的倍数筛选过了。
-
计数素数:遍历
is_prime
数组,计算其中值为true
的元素数量,即为 n n n 以下的素数个数。
完整代码及注释
#include <bits/stdc++.h>
using namespace std;
// 线性筛法求n以下(不包括n)的素数个数
int linsieve(int n){
vector<bool> is_prime(n, true); // 标记数组,初始化所有数为素数
vector<int> primes; // 存放素数的数组
// 0和1不是素数
is_prime[0] = is_prime[1] = false;
for(int i = 2; i < n; i++){
// 如果i是素数,则加入到primes数组中
if(is_prime[i]){
primes.push_back(i);
}
// 用已有的素数去筛i的倍数
for(int j = 0; j < primes.size() && i * primes[j] < n; j++){
is_prime[i * primes[j]] = false; // 标记为非素数
if(i % primes[j] == 0) break; // 如果i能被primes[j]整除,则不再继续筛选
}
}
// 计算素数的个数
int sum = 0;
for(int i = 2; i < n; i++){
if(is_prime[i]){
sum++;
}
}
return sum;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin >> n; // 输入n
cout << linsieve(n) << endl; // 输出n以下的素数个数
return 0;
}
这段代码首先通过线性筛法找出所有 n n n 以下的素数,然后计算并输出这些素数的数量。线性筛的优势在于它减少了重复的筛选工作,因此运行效率较高,尤其适用于求解大范围内的素数问题。
Olympiad
Olympiad
这个问题的核心是要找出给定分数序列中不同非零分数的数量。根据题目描述,我们可以分析出以下几点:
- 分数为0的参与者不能获得奖状。
- 分数不同的参与者被认为是不同的,因此我们只关心不同分数的数量,而不是具体哪些人获得奖状。
- 根据给定的标准,如果某个分数的参与者获得了奖状,那么所有分数高于或等于这个分数的参与者也应该获得奖状。因此,实际上我们只需确定有多少种不同的非零分数即可。
解题步骤:
- 读取输入:首先,我们需要从输入中读取参与者的数量和他们各自的分数。
- 处理分数:将所有非零分数添加到一个集合中,因为集合中的元素是唯一的,这样可以自动去除重复的分数,并且也不会包含0分。
- 计算结果:集合中元素的数量,即为不同非零分数的数量,也就是题目所求的答案。
示例代码
下面是一段解决这个问题的示例代码:
#include <iostream>
#include <set>
using namespace std;
int main() {
int n;
cin >> n;
set<int> scores; // 使用集合来存储分数
for(int i = 0; i < n; ++i) {
int score;
cin >> score;
if(score > 0) { // 只考虑非零分数
scores.insert(score);
}
}
cout << scores.size() << endl; // 输出不同非零分数的数量
return 0;
}
这段代码首先读取参与者数量,然后读取每个参与者的分数,并将非零分数添加到一个集合中。最后,它输出集合的大小,即不同非零分数的数量。
Garden
Garden
这个问题的目的是找到最快完成浇水的方法。我们有一个花园,其长度为
k
k
k,以及
n
n
n 个桶,每个桶可以在一小时内浇水覆盖长度为
a
i
a_i
ai 的连续花园段。我们需要选择一个桶,使得使用这个桶浇完整个花园所需的时间最少。
解题思路
-
遍历桶:对于每个桶,我们检查它是否能被用来浇灌整个花园。这意味着桶的大小 a i a_i ai 必须能够整除花园的长度 k k k,即 k m o d a i = 0 k \mod a_i = 0 kmodai=0。只有当桶的大小能够整除花园的长度时,我们才能使用这个桶浇灌整个花园而不留下未被浇水的部分。
-
计算所需时间:对于可以用来浇灌花园的桶,计算使用它浇灌整个花园所需的时间,即 k / a i k / a_i k/ai。因为我们想要最小化浇灌时间,所以我们应该选择可以使这个值最小的 a i a_i ai。
-
选择最佳桶:从所有可以整除 k k k 的 a i a_i ai 中选择一个,使得 k / a i k / a_i k/ai 最小。这将是浇灌整个花园所需的最少小时数。
算法步骤
- 读入花园的长度 k k k 和桶的数量 n n n,以及每个桶的浇水长度 a i a_i ai。
- 遍历每个桶,检查是否 k m o d a i = 0 k \mod a_i = 0 kmodai=0。对于满足条件的桶,计算 k / a i k / a_i k/ai。
- 保留所有计算出的 k / a i k / a_i k/ai 的值,并找出其中的最小值。
- 输出最小值,这是完成浇水所需的最少小时数。
示例代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<int> buckets(n);
int minHours = INT_MAX;
for (int i = 0; i < n; ++i) {
cin >> buckets[i];
// 检查是否可以整除
if (k % buckets[i] == 0) {
// 计算所需时间并更新最小值
minHours = min(minHours, k / buckets[i]);
}
}
cout << minHours << endl;
return 0;
}
这段代码通过遍历所有桶并检查它们是否能够被花园长度整除来工作。对于可以整除的桶,它计算所需的小时数,并更新最少小时数。最后,它输出完成任务所需的最少时间。
[ARC044A] 素数判定
超时代码展示:
#include<bits/stdc++.h>
using namespace std;
bool is_Prime(int n){
if(n==1) return false;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0) return false;
}
return true;
}
bool is_HuiWen(int n){
string s=to_string(n);
int len=s.size();
for(int i=0;i<len/2;i++){
if(s[i]!=s[len-i-1]) return false;
}
return true;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int l,r;
cin>>l>>r;
for(int i=l;i<=r;i++){
if(is_HuiWen(i)&&is_Prime(i)){
cout<<i<<endl;
}
}
return 0;
}
要分析这段代码的时间复杂度,我们需要逐个看每个函数和主循环。
-
is_Prime
函数: 这个函数检查一个数是否为质数。它做了一个从2遍历到sqrt(n)的循环。因此,is_Prime
函数的时间复杂度为O(sqrt(n))。 -
is_HuiWen
函数: 这个函数检查一个数n是否为回文数。它首先转换这个数为字符串,然后检查字符串是否为回文。由于字符串的长度是数字n的位数D(D = log10(n)),这个函数的循环会运行D/2次。因此,is_HuiWen
函数的时间复杂度为O(D) = O(log(n))。 -
主循环: 主循环遍历从l到r的所有数,对每个数i调用
is_HuiWen
和is_Prime
函数。如果r-l=N,那么主循环将执行N次。因此,主循环的时间复杂度是N乘以这两个函数时间复杂度的和。
综上所述,主循环的时间复杂度为O(N * (sqrt(n) + log(n)))。因为sqrt(n)在大数范围内增长速度快于log(n),所以整体时间复杂度主要受sqrt(n)的影响,我们可以近似表示为O(N*sqrt(n))。
然而,这里的n是循环中的当前数字i,它在l到r之间变化。如果我们考虑最坏情况(即r是最大值),整体时间复杂度可以进一步概括为O(N*sqrt®)。这里的N是数字的范围(r-l+1),r是范围内的最大值。
正确思路展示
为了解决这个问题,我们可以使用线性筛(也称为埃拉托斯特尼筛法的优化版本)来高效地生成范围内的所有质数,然后从这些质数中筛选出回文数。线性筛的好处在于它对每个合数只进行一次筛选,时间复杂度为O(n),这使得它非常适合用于处理大范围内的质数筛选问题。
接下来,我们按照以下步骤进行:
- 使用线性筛生成小于或等于一亿的所有质数。
- 遍历这些质数,检查它们是否是回文数。
- 如果一个质数是回文数,且在给定的区间内,则输出这个数。
线性筛法改进代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100000000;
vector<int> primes;
vector<bool> prime(MAXN + 1, true);
void linearSieve() {
prime[0] = prime[1] = false;
for (int i = 2; i <= MAXN; i++) {
if (prime[i]) {
primes.push_back(i);
}
for (size_t j = 0; j < primes.size() && i * primes[j] <= MAXN; j++) {
prime[i * primes[j]] = false;
if (i % primes[j] == 0) break;
}
}
}
bool isPalindrome(int n) {
string s = to_string(n);
string rev_s = s;
reverse(rev_s.begin(), rev_s.end());
return s == rev_s;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int a, b;
cin >> a >> b;
linearSieve();
for (int i = 0; i < primes.size() && primes[i] <= b; i++) {
if (primes[i] >= a && isPalindrome(primes[i])) {
cout << primes[i] << endl;
}
}
return 0;
}
这段代码首先利用线性筛法预先计算并存储所有小于等于一亿的质数,然后通过简单地遍历这些质数并检查它们是否为回文数来解决原问题。
由于这种方法预计算了所有需要的质数,它能显著减少在给定区间内检查每个数是否为质数的需要,特别是对于大范围数据来说,这种方法能大幅提高效率。
AT_code_festival_relay_a
#include <bits/stdc++.h>
using namespace std;
bool isprime(int x){
if(x<=1) return false;
for(int i=2; i*i<=x; i++){
if(i % 2==0) return false;
}
return true;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,sum = 0;
cin>>n;
for(int i=0; i<n; i++){
if(isprime(i)&&(i % 2==0)){
sum++;
}
}
cout<<sum<<endl;
}
B3750 [信息与未来 2019] 幸运素数
-
素数筛法初始化:
- 使用线性筛法预处理素数表,这是因为线性筛法相较于普通筛法更高效,它确保了每个合数只会被其最小的质因子筛除,从而降低了时间复杂度。
- 初始化一个布尔数组
is_prime
,长度为题目所需的最大值加一(这里设为10000+5
),初始值设为true
。 - 将
0
和1
标记为非素数(即设置is_prime[0]
和is_prime[1]
为false
)。 - 对于每个数,如果它是素数,则记录下来,并用其筛除所有可达的合数。
-
检查幸运素数:
- 从用户输入的起始值
m
到结束值n
进行遍历。 - 对于每个数
i
,对它进行处理,逐步去除最低位,检查每一步得到的数是否为素数。 - 如果在去除过程中发现某个数不是素数,则此数
i
不是幸运素数,停止进一步的检查。 - 如果直到变为一位数都没有发现非素数,那么这个数
i
就是幸运素数。
- 从用户输入的起始值
-
输出结果:
- 对于每个检查过程,如果判定为幸运素数,则输出这个数。
这样的处理保证了高效地找出指定范围内的所有幸运素数,时间复杂度主要取决于线性筛的预处理以及随后对每个数检查的过程。由于线性筛的时间复杂度是 O(n),且随后对每个数的判断过程也相当高效,因此整体解法是适合处理这类问题的。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10000 + 5;
vector<bool> is_prime(MAXN, true);
vector<int> primes;
void linevie() {
is_prime[0] = is_prime[1] = false;
for (int i = 2; i < MAXN; i++) {
if (is_prime[i]) {
primes.push_back(i);
}
for (int j = 0; j < primes.size() && i * primes[j] < MAXN; j++) {
is_prime[i * primes[j]] = false;
if (i % primes[j] == 0) break;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
linevie(); // 初始化素数筛
int start, end;
cin >> start >> end;
for (int i = start; i <= end; i++) {
int sum = i;
bool flag = true;
while (sum > 0) {
if (!is_prime[sum]) {
flag = false;
break;
}
if (sum < 10) break; // 如果 sum 为一位数,则结束循环
sum /= 10;
}
if (flag) {
cout << i << '\n';
}
}
return 0;
}
P1304 哥德巴赫猜想
P1304 哥德巴赫猜想
使用线性筛法来解决哥德巴赫猜想问题时,我们可以分为以下几个步骤来思考和实现:
-
线性筛生成质数:
- 初始化一个足够大的范围(例如0到N),用于找出所有小于等于N的质数。
- 使用一个数组记录每个数字是否是质数,并同时构建一个质数列表。
- 遍历每个数,对于每个未被标记为合数的数,将其视为质数并加入质数列表。
- 遍历质数列表,用当前质数乘以列表中的每个质数,标记得到的产品为合数,并确保每个合数只被其最小质因数筛选一次。
-
检验哥德巴赫猜想:
- 对于给定范围内的每个偶数(从4到N),尝试将其分解为两个质数之和。
- 遍历质数列表,对于每个质数p,检查
偶数-p
是否也是质数。 - 如果找到这样的一对质数,则记录下来并结束对当前偶数的检查,转而检验下一个偶数。
-
输出结果:
- 根据题目要求,输出每个偶数及其分解为两个质数之和的结果。
- 如果一个偶数有多种分解方法,只需输出第一对质数中第一个质数最小的组合。
解答实现
基于以上思路,下面是一个C++程序的实现:
#include <iostream>
#include <vector>
using namespace std;
vector<int> linearSieve(int N) {
vector<int> primes; // 用于存储所有的质数
vector<bool> isPrime(N + 1, true); // 标记是否是质数
for (int i = 2; i <= N; ++i) {
if (isPrime[i]) {
primes.push_back(i); // 是质数则加入列表
}
for (int j = 0; j < primes.size() && i * primes[j] <= N; ++j) {
isPrime[i * primes[j]] = false; // 标记合数
if (i % primes[j] == 0) break; // 保证每个合数只被其最小质因数筛选一次
}
}
return primes;
}
void goldbach(int N) {
vector<int> primes = linearSieve(N);
for (int i = 4; i <= N; i += 2) {
for (int p : primes) {
if (p > i / 2) break;
int diff = i - p;
if (binary_search(primes.begin(), primes.end(), diff)) {
cout << i << "=" << p << "+" << diff << endl;
break; // 找到第一对后即停止
}
}
}
}
int main() {
int N;
cin >> N; // 读取N
goldbach(N); // 进行哥德巴赫猜想检验
return 0;
}
这段代码首先通过线性筛法生成了一个质数列表,然后用这个列表来验证每个偶数是否能表示为两个质数之和,并按要求输出结果。
P1218 [USACO1.5] 特殊的质数肋骨 Superprime Rib
P1218 [USACO1.5] 特殊的质数肋骨 Superprime Rib
简单方法:
为了解决这个问题,我们需要采用递归的方法。思路是这样的:
- 基础情况:当我们找到一个长度为
n
的质数时,我们将其输出。 - 递归步骤:对于每一个数字,我们尝试添加一个数字(从1到9),并检查新数字是否为质数。如果是,我们继续递归搜索。
我们首先需要一个函数来检查一个数字是否为质数。然后,我们编写递归函数,从单个数字(2, 3, 5, 7)开始,因为一个特殊质数的最右边的数字必须是质数。
下面是C++代码实现,包含必要的注释:
#include <iostream>
#include <cmath>
using namespace std;
// 检查数字是否为质数
bool isPrime(int num) {
if (num < 2) return false;
for (int i = 2; i <= sqrt(num); ++i) {
if (num % i == 0) return false;
}
return true;
}
// 递归寻找特殊质数
void findSuperPrimes(int num, int n, int depth) {
if (depth == n) { // 如果达到所需的长度,输出数字
cout << num << endl;
return;
}
for (int i = 1; i <= 9; ++i) { // 尝试添加每一个数字
int newNum = num * 10 + i; // 生成新数字
if (isPrime(newNum)) { // 如果新数字是质数
findSuperPrimes(newNum, n, depth + 1); // 递归搜索
}
}
}
int main() {
int n;
cin >> n; // 读取长度
// 从每一个单个的质数开始搜索
findSuperPrimes(2, n, 1);
findSuperPrimes(3, n, 1);
findSuperPrimes(5, n, 1);
findSuperPrimes(7, n, 1);
return 0;
}
这段代码的关键是findSuperPrimes
函数,它递归地构建特殊质数并在达到所需长度时输出它们。通过从2、3、5、7这四个数字开始,确保了我们始终是从一个质数开始构建的。在递归过程中,我们在每一步都检查所构建的数字是否仍然是质数,这样确保了输出的每个数字都是特殊质数。
线性筛法:
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 100000; // 设置足够大的范围以筛选质数
vector<bool> is_prime(MAXN + 1, true); // 初始化所有数字为质数
// 线性筛生成质数表
void linearSieve() {
is_prime[0] = is_prime[1] = false; // 0和1不是质数
for (int i = 2; i <= MAXN; ++i) {
if (is_prime[i]) {
for (int j = 2 * i; j <= MAXN; j += i) {
is_prime[j] = false; // 标记i的所有倍数为非质数
}
}
}
}
// 递归寻找特殊质数
void findSuperPrimes(int num, int n, int depth) {
if (depth == n) {
cout << num << endl;
return;
}
for (int i = 1; i <= 9; ++i) {
int newNum = num * 10 + i;
if (is_prime[newNum]) { // 直接使用is_prime数组判断是否为质数
findSuperPrimes(newNum, n, depth + 1);
}
}
}
int main() {
int n;
cin >> n;
linearSieve(); // 初始化质数表
// 从每个可能的单个数字质数开始搜索
findSuperPrimes(2, n, 1);
findSuperPrimes(3, n, 1);
findSuperPrimes(5, n, 1);
findSuperPrimes(7, n, 1);
return 0;
}
B3738 [信息与未来 2018] 素数方阵
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e8;//这里不要错误的填写成400多
vector<int> primes;
vector<bool> is_prime(MAXN, true);
void Linear_sieve() {
is_prime[0] = is_prime[1] = false;
for (int i = 2; i < MAXN; i++) {
if (is_prime[i]) {
primes.push_back(i);
}
for (int j = 0; (j < primes.size()) && (i * primes[j] < MAXN); j++) {
is_prime[i * primes[j]] = false;
if (i % primes[j] == 0) break;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, x, y;
cin >> n >> x >> y;
Linear_sieve(); // 线性筛生成素数
vector<vector<int>> matrix(n, vector<int>(n, 0)); // 定义方阵
// 方向数组,分别对应右、下、左、上
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
int dir = 0; // 初始方向为右
int px = 0, py = 0; // 起始位置
for (int i = 0; i < primes.size() && i < n*n; ++i) {
matrix[px][py] = primes[i]; // 填充素数
int nx = px + dx[dir], ny = py + dy[dir]; // 尝试下一个位置
// 检查是否需要改变方向:边界检查或下一个位置已填充
if (nx < 0 || nx >= n || ny < 0 || ny >= n || matrix[nx][ny] != 0) {
dir = (dir + 1) % 4; // 改变方向
nx = px + dx[dir];
ny = py + dy[dir];
}
px = nx;
py = ny;
}
// 打印填充的数组
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cout << setw(5) << matrix[i][j] << " "; // 使用setw(5)保证输出对齐
}
cout << endl;
}
return 0;
}
采用while循环模拟行走路径的填充方法
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e8;
vector<int> primes;
vector<bool> is_prime(MAXN, true);
// 线性筛生成素数
void Linear_sieve() {
is_prime[0] = is_prime[1] = false;
for (int i = 2; i < MAXN; i++) {
if (is_prime[i]) {
primes.push_back(i);
}
for (int j = 0; j < primes.size() && i * primes[j] < MAXN; j++) {
is_prime[i * primes[j]] = false;
if (i % primes[j] == 0) break;
}
}
}
// 检查是否可以在当前方向上进行填充
bool canFill(int x, int y, int n, vector<vector<int>>& matrix) {
return x >= 0 && x < n && y >= 0 && y < n && matrix[x][y] == 0;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, x, y;
cin >> n >> x >> y;
Linear_sieve(); // 生成素数
vector<vector<int>> matrix(n, vector<int>(n, 0));
int idx = 0; // 素数索引
int px = 0, py = 0; // 当前位置
matrix[px][py] = primes[idx++]; // 初始化方阵的第一个元素
// 填充方阵
while(idx < n*n) {
// 向右填充
while(canFill(px, py+1, n, matrix)) matrix[px][++py] = primes[idx++];
// 向下填充
while(canFill(px+1, py, n, matrix)) matrix[++px][py] = primes[idx++];
// 向左填充
while(canFill(px, py-1, n, matrix)) matrix[px][--py] = primes[idx++];
// 向上填充
while(canFill(px-1, py, n, matrix)) matrix[--px][py] = primes[idx++];
}
// 输出整个方阵
cout << matrix[x - 1][y - 1] << endl; // 输出指定位置的素数
return 0;
}
[ABC284D] Happy New Year 2023
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
for (int i = 0; i < t; ++i) {
long long N;
cin >> N;
long long A;
for (long long j = 2; j * j * j <= N; ++j) {
if (N % j == 0) {
A = j;
break;
}
}
long long p, q;
if (N % (A * A) == 0) {
p = A;
q = N / A / A;
} else {
p = sqrt(N / A);
q = A;
}
cout << p << " " << q << endl;
}
return 0;
}
这种方法是通过观察 N = p 2 q N = p^2q N=p2q 的形式,利用因子关系来求解 p p p 和 q q q 的值。
首先,我们可以发现,如果将 N N N 分解为两个因子 A A A 和 B B B,即 N = A B N = AB N=AB,那么有以下两种情况:
- A = p 2 A = p^2 A=p2, B = q B = q B=q
- A = p A = p A=p, B = p q B = pq B=pq
基于这个观察,我们可以通过枚举 A A A 的值来确定 p p p 和 q q q。
具体步骤如下:
- 枚举 A A A 的值,从 2 2 2 开始,直到 A 3 l e q N A^3 \\leq N A3leqN。
- 对于每个枚举的 A A A 值,判断 N N N 是否能被 A A A 整除,即 N N \\% A == 0 N。
- 如果找到满足条件的 A A A 值,则退出循环。
接下来,根据找到的 A A A 值,判断属于哪种情况:
- 如果 N N N 能被 A 2 A^2 A2 整除,即 N N \\% (A \\times A) == 0 N,那么说明 A = p A = p A=p, B = p q B = pq B=pq。此时, p = A p = A p=A, q = N / A 2 q = N / A^2 q=N/A2。
- 否则,说明 A = p 2 A = p^2 A=p2, B = q B = q B=q。此时, p = s q r t N / A p = \\sqrt{N / A} p=sqrtN/A, q = A q = A q=A。
最后,输出
p
p
p 和
q
q
q 的值。
这种方法的时间复杂度为
O
(
T
×
N
3
)
O(T \times \sqrt[3]{N})
O(T×3N),其中
T
T
T 为测试用例的数量。枚举
A
A
A 的时间复杂度为
O
(
N
3
)
O(\sqrt[3]{N})
O(3N),因为
A
3
≤
N
A^3 \leq N
A3≤N。判断
N
N
N 是否能被
A
A
A 整除的时间复杂度为
O
(
1
)
O(1)
O(1)。
与之前的方法相比,这种方法减少了判断质数的步骤,从而降低了时间复杂度。空间复杂度与之前的方法相同,为 O ( 1 ) O(1) O(1),只使用了常数级别的额外空间。这种方法利用了因子关系,通过枚举 A A A 的值来确定 p p p 和 q q q,避免了判断质数的过程,从而提高了效率。