题目链接

素数三元组

题目描述

给定一个正整数 ,我们称有序三元组 为素数三元组,当且仅当满足下述两条:

  1. 均为素数;

请你计算,不超过 的素数中,一共有多少个不同的素数三元组 满足上式。

解题思路

这是一个需要结合数论知识和高效算法来解决的计数问题。直接暴力枚举所有不超过 的三个素数组合会非常慢,我们需要找到更优化的方法。

  1. 利用奇偶性进行分析

    一个关键的突破口在于分析素数的奇偶性。我们知道,唯一的偶素数是 ,其他所有素数都是奇数。

    • 情况一 都是奇素数。此时它们的和 是一个偶数。如果 ,那么 也必须是偶数,这意味着 必须是偶数。唯一的偶素数是 ,所以 。方程变为 。不存在两个奇素数的和为 不是素数),因此这种情况不成立。
    • 情况二 中至少有一个是

    通过这个分析,我们得出结论:任何满足条件的素数三元组中, 之中必定有一个是

  2. 简化问题

    由于三元组是有序的,我们需要考虑 两种情况。这两种情况都将原方程 简化为了 的形式,其中 是另一个素数。

    我们可以改写方程为

  3. 算法设计

    基于以上分析,我们可以设计出高效的算法:

    • 预处理:首先,使用埃拉托斯特尼筛法(埃氏筛)预处理出一个布尔数组 ,标记出从 范围内的所有素数。
    • 遍历:遍历所有不超过 的素数
    • 计算和检验:对于每一个素数 ,我们计算出 的值。
    • 然后,我们检查这个计算出的 是否也是一个不超过 的素数。这可以通过查询预处理好的 数组在 时间内完成。
    • 计数
      • 如果 是一个合法的素数(即 且为素数),我们就找到了满足条件的素数对
      • 如果 ,这对应着三元组 。这种情况只会计数一次。
      • 如果 ,这对应着两个有序三元组:。因此计数需要加
    • 优化:在遍历 的过程中,一旦发现 ,就可以提前终止循环,因为后续的 只会使这个差值变得更大。

代码

#include <iostream>
#include <vector>

using namespace std;

int main() {
    int N;
    cin >> N;

    vector<bool> is_prime(N + 1, true);
    is_prime[0] = is_prime[1] = false;
    for (int p = 2; p * p <= N; ++p) {
        if (is_prime[p]) {
            for (int i = p * p; i <= N; i += p) {
                is_prime[i] = false;
            }
        }
    }

    long long count = 0;
    for (int p3 = 2; p3 <= N; ++p3) {
        if (is_prime[p3]) {
            long long p_other = (long long)p3 * p3 - 2;
            if (p_other > N) {
                break;
            }
            if (p_other > 0 && is_prime[p_other]) {
                if (p_other == 2) {
                    count++;
                } else {
                    count += 2;
                }
            }
        }
    }

    cout << count << endl;
    return 0;
}
import java.util.Scanner;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();

        boolean[] is_prime = new boolean[N + 1];
        Arrays.fill(is_prime, true);
        is_prime[0] = is_prime[1] = false;
        for (int p = 2; p * p <= N; p++) {
            if (is_prime[p]) {
                for (int i = p * p; i <= N; i += p) {
                    is_prime[i] = false;
                }
            }
        }

        long count = 0;
        for (int p3 = 2; p3 <= N; p3++) {
            if (is_prime[p3]) {
                long p_other = (long)p3 * p3 - 2;
                if (p_other > N) {
                    break;
                }
                if (p_other > 0 && is_prime[(int)p_other]) {
                    if (p_other == 2) {
                        count++;
                    } else {
                        count += 2;
                    }
                }
            }
        }
        System.out.println(count);
    }
}
def main():
    N = int(input())
    
    is_prime = [True] * (N + 1)
    is_prime[0] = is_prime[1] = False
    for p in range(2, int(N**0.5) + 1):
        if is_prime[p]:
            for i in range(p * p, N + 1, p):
                is_prime[i] = False
    
    count = 0
    for p3 in range(2, N + 1):
        if is_prime[p3]:
            p_other = p3 * p3 - 2
            if p_other > N:
                break
            
            if p_other > 0 and is_prime[p_other]:
                if p_other == 2:
                    count += 1
                else:
                    count += 2
                    
    print(count)

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:数论(质数筛法、奇偶性分析)

  • 时间复杂度:预处理阶段使用埃氏筛法,时间复杂度为 。后续遍历所有不超过 的素数来寻找三元组,这部分操作的次数远小于 ,可以近似看作 。因此,总时间复杂度由筛法主导,为

  • 空间复杂度:需要一个布尔数组来存储每个数是否为质数,空间复杂度为