小卡与质数系列QAQ

124 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

今天来更新跟去年传智杯初赛有关的两道题,分别是小卡与质数1和小卡与质数2。说实话,做这两道题花费了我不少时间,虽然去年的传智杯比较水(QAQQAQ),但是这两题还是学到了不少东西,补了补我比较薄弱的方面,话不多说,放题目了。

[传智杯 #4 初赛] 小卡和质数

题目背景

小卡最近迷上了质数,所以他想到了一个和质数有关的问题来考考你。

质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

题目描述

小卡有 T(1T105)T(1\le T\le 10^5) 组询问。每次询问给你两个正整数 x,y(1x,y109)x,y(1\le x,y\le 10^9)

小卡想知道,第 xx 个质数和第 yy 个质数是否满足 pxpy=1p_x \oplus p_y =1,即第 xx 个质数和第 yy 个质数的异或值是否是11

输入格式

第一行一个正整数 TT,表示询问的数量。

接下来 TT 行,每行两个正整数 x,yx,y,表示询问的是第 xx 个质数和第 yy 个质数。

输出格式

TT 行,每行一个字符串YesNo,分别表示两个质数的异或值是 11 或不是 11

样例 #1

样例输入 #1

4
1 2
23 145
66 2
1 14

样例输出 #1

Yes
No
No
No

分析

其实我的位运算这块特别薄弱,所以一般看到位运算的题目,我就放弃,看到这题,其实我内心也想放弃,但是后来我试了几个数,发现除了2和3两个数异或值为1,其他的质数异或值都不为1,所以果断猜测,只有这一种为1的情况,然后提交ACAC了,后来上网一搜,发现只有相邻的两个数且奇数大的情况下,异或值为1。而质数,只有2,32,3这一种情况满足,所以为只有这一种YESYES

代码

#include <iostream>
#include <string> 
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cstring>
#include <stack> 
#include <cmath>
#include <algorithm>
using namespace std;
int cnt[110][1100],m[1100];
signed main(){
	int t;
	cin>>t;
	while(t--){
		int x,y;
		cin>>x>>y;
		if((x==2 && y==1) || (x==1 && y==2)) puts("Yes");
		else puts("No"); 
	}
	//cout<<p<<endl;
}

下面是第二题小卡与质数2

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

题目背景

小卡迷上了质数!

题目描述

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

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

9
5
6
7
8
9
10
100
1000
10000

样例输出 #1

2
4
4
2
2
4
22
163
1132

其实在第一题之后,我又去了解了一下异或的其他性质,异或其实就是把两个数转化为二进制之后,对比两个二进制数相应的位,遵循同为0,异为1的原则,比如一个数和本身异或结果就是0,然后一个偶数^1=偶数+1,ab=ca\oplus b =c,可以推出ac=ba\oplus c=b,这题就用到了这个性质,还有个重要的性质,就是aba+ba \oplus b≤a+b

分析

这题我最先的想法是先用线性筛把2到200000之间所有的质数全部筛出来,然后运用上面那个性质,如果xprimes[i]xx \oplus primes[i]≤x,那么答案ans++ans++。我本来以为这题的TT个询问就跟codeforcescodeforces里面一样,不计入时间复杂度,但是是事实计入,所以我的这个代码复杂度是O(Tn/lnn)O(Tn/lnn)显然超时,QAQQAQ,于是乎,我只能取评论区翻了一下,发现了一种奇妙的做法,只需要把我这个思路添加一点,我们很显然发现,要xprimes[i]xx \oplus primes[i]≤x,我们就要想primes[i]primes[i]要满足什么样的条件呢,假设primes[i]primes[i]的二进制最高位为mm(一定是1),如果xx所对应的二进制数的第mm位是0,假设xx的二进制表示最高位比mm大,那么这一位异或结果是1,再看高于mm的其他位,因为primes[i]primes[i]的更高位肯定是0,所以xx的二进制表示的更高位就是和0异或,那么异或值肯定=本身。所以这个异或结果一定>xx。而反过来分析就会发现,如果xx所对应的二进制数的第mm位是1,那么显然x>x,因此我们只需要统计每个质数的最高位,用一个数组res[i]res[i]记录最高位为ii且对应值为1的质数个数。然后我们只要遍历xx二进制位,如果这个位置为1,那么我们就加上相应的resres值。我们可以把resres的空间开大一点,开到31那么其实最后的复杂度就是O(31T)O(31T),不会超时。 那么问题又来了,怎么快速找出一个二进制数的最高位是否为1呢,在今天之前我也不会,翻阅了大佬的AcwingAcwing发现了一个方法,如下图:

2022-11-25.png 最后要记住,二进制是默认从0开始,所以最高位真正对应的要-1,最后奉上代码。

代码

#include <iostream>
#include <string> 
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cstring>
#include <stack> 
#include <cmath>
#include <algorithm>
using namespace std;
const int N=2000020;
int primes[N];
bool st[N];
int cnt=0;
int p=0;
int res[32];
inline void prime(int n){//线性筛 
	for(int i=2;i<=n;i++){
		if(!st[i]){
			primes[++cnt]=i;
			p++;	
		}
		for(int j=1;primes[j]<=n/i;j++){
			st[primes[j]*i]=1;
			if(i%primes[j]==0) break;
		}
	}
	for(int i=1;i<=cnt;i++){
		for(int j=30;j;j--){
			if(primes[i]&(1<<(j-1))){
				res[j]++;
				break;
			}
		}
	}
}
signed main(){
	int x;
	st[0]=1;
	st[1]=1;
	prime(N-10);
	//cout<<p;
	int t;
	cin>>t;
	while(t--){
		int x;
		scanf("%d",&x);
		int ans=0;
		for(int i=30;i;i--){
			if(x&(1<<(i-1))) ans+=res[i];
		}
		printf("%d\n",ans);
	}
}

希望能帮助到大家(QAQQAQ)!