第四届传智杯初赛_小卡与质数2

19 篇文章 2 订阅

[传智杯 #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 ⊕ y = p ( y < x ) x⊕y=p (y < x) xy=p(y<x)
要枚举 y y y,时间复杂度 O ( T x ) O(Tx) O(Tx)
x ⊕ y = p → x ⊕ p = y ( y < x ) x⊕y=p \to x⊕p=y(y < x) xy=pxp=y(y<x)
要枚举 p p p ,时间复杂度 O ( T z ) O(Tz) O(Tz) 。百万内的质数有 z z z个(几万)。
因为 y < x y<x y<x,异或后值变小。什么情况异或后值会变小?
x = ( 100100 ) 2 x=(100100)_2 x=(100100)2
要想异或后值变小, 1 1 1必须变成 0 0 0

1 ⊕ 1 = 0 1⊕1=0 11=0

  • 第一个 1 1 1
    100100 ⊕ 1 ∗ ∗ ∗ ∗ ∗ = 0 ∗ ∗ ∗ ∗ ∗ < 100100 100100⊕ 1***** = 0***** <100100 1001001=0<100100
    换言之, 100100 ⊕ ( 100000 ∼ 111111 ) 100100⊕(100000 \sim 111111) 100100(100000111111)的值都小于 100100 100100 100100
    即, x ⊕ ( 2 5 ∼ 2 6 − 1 ) x⊕(2^5 \sim 2^6-1) x(25261)
    的值小于 x x x
    现在我们只要算出 ( 2 5 ∼ 2 6 − 1 ) (2^5 \sim 2^6-1) (25261)
    有几个质数即可,前缀和。

  • 第二个 1 1 1
    100100 ⊕ 0001 ∗ ∗ = 1000 ∗ ∗ < 100100 100100 ⊕ 0001** = 1000** <100100 1001000001=1000<100100
    换言之, 100100 ⊕ ( 000100 ∼ 000111 ) 100100⊕(000100 \sim 000111) 100100(000100000111) 的值都小于 100100 100100 100100
    即, x ⊕ ( 2 2 ∼ 2 3 − 1 ) x⊕(2^2 \sim 2^3-1) x(22231)
    的值小于 x x x
    现在我们只要算出 ( 2 2 ∼ 2 3 − 1 ) (2^2 \sim 2^3-1) (22231) 有几个质数即可。

总之,若 x x x 的第 i i i 位为 1 1 1,算出 2 i ∼ 2 i + 1 − 1 2^i \sim 2^{i+1}-1 2i2i+11有多少个质数累加即可。

注意, x ⊕ p = y ( y < x ) x⊕p=y(y < x) xp=y(y<x) p p p 有可能超过 x x x

代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2e6 + 10;

bool st[N];
int s[N];

int lowbit(int x)
{
	return x & -x;
}

void get_prefix()
{
	st[0] = st[1] = 1;
	for(int i = 2; i <= N - 10; i ++)
	{
		if(st[i]) continue;
		for(int j = i + i; j <= N - 10; j += i) st[j] = 1;
	}
	
	for(int i = 1; i <= N - 10; i ++) s[i] = s[i - 1] + (!st[i]);
}

int main()
{
	
	get_prefix();
	
	int T;
	scanf("%d", &T);
	while(T --)
	{
		int x;
        scanf("%d", &x);
        
        int res = 0;
        while(x)
        {
        	int t = lowbit(x);
        	res += s[(t << 1) - 1] - s[t - 1];
			x -= t;
		}
		cout << res << endl;
	}
	
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值