小明正在参加魔法科的期末考试,考生需要根据给定的口诀组合出有效的魔法。其中,老师给定了 nn 个上半部分口诀 a1,a2,...,ana1,a2,...,an 和 mm 个下半部分口诀 b1,b2,...,bmb1,b2,...,bm,均用整数表示。完整的口诀包含一个上半部分口诀和一个下半部分口诀,当选用两个口诀 aiai 和 bjbj,将组合出完整口诀 S=ai+bjS=ai+bj。
当 SS 满足 S≤n+mS≤n+m 且 SS 为质数时,魔法是有效的。魔法的种类只和 SS 的大小有关。如果每个上半部分口诀和每个下半部分口诀在不同的组合中可以重复使用,小明想知道一共可能组合出多少种不同的有效魔法?
输入格式
输入共三行。
- 第一行为两个正整数 n,mn,m。
- 第二行为 nn 个由空格分开的正整数 a1,a2,...,ana1,a2,...,an。
- 第三行为 mm 个由空格分开的正整数 b1,b2,...,bmb1,b2,...,bm。
输出格式
输出共 11 行,一个整数表示答案。
样例输入
3 4
2 3 10
3 4 5 1
样例输出
3
样例说明
可以组合出 3,5,73,5,7 这三个有效魔法。
java
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
int[] n1 = new int[n];
int[] m1 = new int[m];
for (int i = 0; i < n; i++) {
n1[i] = in.nextInt();
}
for (int i = 0; i < m; i++) {
m1[i] = in.nextInt();
}
in.close();
// 优化点 1: 使用筛法预处理素数,时间复杂度 O((n+m) log log (n+m))
boolean[] isPrime = sieve(n + m);
// 优化点 2: 将其中一个数组存入HashSet,用于O(1)时间复杂度的查找
Set<Integer> setN = new HashSet<>();
for (int num : n1) {
setN.add(num);
}
Set<Integer> resultSet = new HashSet<>();
// 遍历第二个数组
for (int num : m1) {
// 遍历所有可能的素数 p
// p 的最小值为 2 (1+1),最大值为 n+m
for (int p = 2; p <= n + m; p++) {
if (isPrime[p]) { // 如果 p 是素数
int complement = p - num;
// 检查 complement 是否在第一个数组中存在
if (setN.contains(complement)) {
resultSet.add(p);
}
}
}
}
System.out.println(resultSet.size());
}
/**
* 埃拉托斯特尼筛法,用于找出 0 到 max 之间的所有素数。
* @param max 筛法的上限
* @return 一个布尔数组,isPrime[i] 表示 i 是否为素数
*/
private static boolean[] sieve(int max) {
if (max < 2) {
return new boolean[0]; // 没有素数
}
boolean[] isPrime = new boolean[max + 1];
Arrays.fill(isPrime, true);
isPrime[0] = isPrime[1] = false;
// 只需遍历到 sqrt(max)
for (int i = 2; i * i <= max; i++) {
if (isPrime[i]) { // 如果 i 是素数,那么它的倍数都不是素数
// 从 i*i 开始标记,因为更小的倍数已经被标记过了
for (int j = i * i; j <= max; j += i) {
isPrime[j] = false;
}
}
}
return isPrime;
}
}