原题
题目背景
小卡迷上了质数!
题目描述
小卡最近迷上了质数,所以他想把任何一个数都转化为质数!
小卡有 T T T 次询问,每次给你一个数字 x x x,问有多少个比 x x x 小的非负整数 y y y,使得 x ⊕ y x\oplus y x⊕y 是质数,其中 ⊕ \oplus ⊕ 表示按位异或。
输入格式
第一行一个正整数 T ( 1 ≤ T ≤ 1 0 5 ) T(1\le T\le10^5) T(1≤T≤105),表示有 T T T 组询问。
接下来 T T T 行,每行一个正整数 x ( 1 ≤ x ≤ 1 0 6 ) x(1\le x\le 10^6) x(1≤x≤106)。
输出格式
对于每组询问,输出一行一个整数,表示答案。
样例 #1
样例输入 #1
9
5
6
7
8
9
10
100
1000
10000
样例输出 #1
2
4
4
2
2
4
22
163
1132
题目分析
可以观察到,x最大是106, 所以如果暴力枚举所有小于2x的数(y)则一定会超时。
优化操作1
这时候我们可以尝试反过来枚举,因为 假设x ^ y == z 那么 x ^ y == z,所以我们可以转而枚举z, 计算可得在2x的范围内z的数量大约是几万个,此时程序的时间复杂度可以达到104 * 106 ==1010 ,还是会超时。
优化操作2
转而分析何时 z ^ x == y < x
如果z的二进制中最高位是第 hh 位,考虑 x 的二进制中的每一位,当且仅当 x 的第 h 位上也是1的时候,k^ x 的结果小于 x。
答案代码
//
// Created by zhangfan on 2022/11/23.
//
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1000010;
int x, t;
int cnt;
int primes[N];
bool st[N];
int judge[22][N], counts[22];//2 * 10^6最大只有21位所以开一个22维的数组
//线性筛法求质数
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 ++)
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
//按最高位1的位置来将质数分类
void divide_primes()
{
for (int i = 0; i < cnt; i++)
{
int c = -1, temp = primes[i];
while (temp)
{
temp >>= 1;
c++;
}
judge[c][counts[c]++] = primes[i];
}
}
int main()
{
cin >> t ;
get_primes(2000010);
divide_primes();
while (t--)
{
cin >> x;
int res = 0;
for (int i = 0; i < 22; i++)
{
if(x & (1 << i))
res += counts[i];//挑选符合的答案
}
cout << res << endl;
}
return 0;
}