P8842 [传智杯 #4 初赛] 小卡与质数2 题解

原题

原题链接

题目背景

小卡迷上了质数!

题目描述

小卡最近迷上了质数,所以他想把任何一个数都转化为质数!

小卡有 T T T 次询问,每次给你一个数字 x x x,问有多少个比 x x x 小的非负整数 y y y,使得 x ⊕ y x\oplus y xy 是质数,其中 ⊕ \oplus 表示按位异或。

输入格式

第一行一个正整数 T ( 1 ≤ T ≤ 1 0 5 ) T(1\le T\le10^5) T(1T105),表示有 T T T 组询问。

接下来 T T T 行,每行一个正整数 x ( 1 ≤ x ≤ 1 0 6 ) x(1\le x\le 10^6) x(1x106)

输出格式

对于每组询问,输出一行一个整数,表示答案。

样例 #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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值