小卡与质数2
题目地址
https://www.luogu.com.cn/problem/T216909?contestId=58544题目背景
题目描述
小卡有 TTT 次询问,每次给你一个数字 xxx,问有多少个比 xxx 小的非负整数 yyy,使得 x⊕yx\oplus yx⊕y 是质数,其中 ⊕\oplus⊕ 表示按位异或。
输入输出格式
输入格式
接下来 TTT 行,每行一个正整数 x(1≤x≤106)x(1\le x\le 10^6)x(1≤x≤106)。
输出格式
输入输出样例
输入样例 #1
9
5
6
7
8
9
10
100
1000
10000
输出样例 #1
2
4
4
2
2
4
22
163
1132
思路:
x⊕yx\oplus yx⊕y是质数,y<xy< xy<x,那么假设x的二进制位为1010,如果y在x二进制为1的位上为0,那么y就一定小于x,即y的二进制为0***,100*,表示任意数字。 1010和0**异或后的数是质数,大小范围一定在[8,15][8, 15][8,15]之间,那么就看8-15之间有多少质数;1010和100异或后,质数大小范围一定在[2, 3], 那么看2-3之间有多少质数。
因此我们可以发现x的二进制有k位,第i位为1,那么y在前i-1位和x保持一致,第i位为0,后续数字任意排列,都会比x小;这样 x⊕yx\oplus yx⊕y的质数一定在[2i,2i∗2−1][2^i, 2^i *2 - 1][2i,2i∗2−1]
算法实现
首先预处理出质数,由于异或后的数字可能比x大, 但总会小于2x2x2x,所以预处理的质数范围设定在2000000,之后利用前缀和再预处理出1-2000000之间有多少质数。对于每个询问t,分别求出x的二进制位,如果第i位为1,那么必然存在小于x的数能够异或出[2i,2i∗2−1][2^i, 2^i *2 - 1][2i,2i∗2−1]之间的质数,因此利用前缀和查询对应区间质数的数量。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e6 + 10;
int primes[N], cnt;
bool st[N];
int sum[N];
void get_primes(int n) // 线性筛质数
{
for(int i = 2; i <= n; i ++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; i * primes[j] <= n; j ++)
{
st[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
get_primes(2000000); // 预处理质数
for(int i = 2; i <= 2000000; i ++)
{
if(!st[i]) sum[i] = sum[i - 1] + 1;
else sum[i] = sum[i - 1];
}
int t, x;
scanf("%d", &t);
while(t --)
{
scanf("%d", &x);
int ans = 0;
for(int i = 31; i >= 0; i --) // 枚举第i位是否为1
{
if(x >> i & 1)
{
int a = 1 << i, b = (1 << (i + 1)) - 1;
ans += sum[b] - sum[a - 1]; // 查询[2 ** i, 2 ** (i + 1) - 1]区间有多少个质数
}
}
printf("%d\n", ans);
}
return 0;
}
这篇文章讲解了小卡如何通过质数异或性质解决质数查询问题,涉及二进制分析、位操作和质数计数技巧。通过预处理质数和前缀和,快速计算每个询问下符合条件的质数数量。
678





