问题描述
在一个神秘的世界里,坤坤正在玩一个古老的魔法游戏。这个游戏的目标是将一些神秘的咒语石头分组。每个咒语石头都有一个独特的编号,范围是从 2 到 𝑁。在每一步,坤坤可以选择两个独特的咒语石头,如果他们共享一个大于 1 的共同因子,就可以将它们归入同一组。坤坤会一直将石头进行分组,直到不能再进行分组为止。
在这个游戏中,归属于一个组的石头形成了等价关系。这意味着如果石头 𝑎 和石头 𝑏 在同一组,石头 𝑏 和石头 𝑐 也在同一组,那么石头 𝑎 和石头 𝑐 就被认为是在同一组。
/* 例如:a=2,b=6,c=3 a,b有共同因子2(a,b在同一组),b,c有共同因子3 (b,c在同一组),所以a,c视为在同一组,既a,b,c在同一组 */
坤坤的任务是找出最后形成的组的总数。
输入格式
第一行将包含一个整数 𝑇,表示测试案例的数量。然后是 𝑇 行输入,每行包含一个整数 𝑁。
数据范围保证:1≤𝑇≤2×1e5,1≤T≤2×1e5,2≤𝑁≤1e7,2≤N≤1e7。
C++ 时间限制:2s,空间限制:256MB
输出格式
对于每个测试案例,输出一行,表示最后形成的组的总数。
样例输入
3
2
4
8
样例输出
1
2
3
说明
测试案例 1:最后的组是 { 2 }。
测试案例 2:最后的组是 { 2,4 } 和 { 3 }。
测试案例 3:最后的组是 { 2,3,4,6,8},{ 5 } 和 { 7 }。
思路分析:
首先,所有的偶数都有共同因子2,最终一定能分到一组里,所以我们只需判断奇数部分最终能生成多少组。
例子:N==10,既 {2,3,4,5,6,7,8,9,10},奇数{3,5,7,9}
1.{2,4,6,8,10} //所有偶数成为一组
2. 第一个奇数3,与上面的组中的2和6形成了等价关系,所以3与它们分为一组既{2,3,4,6,8,10}
3. 第二个奇数5,与上面的组中的2和10形成了等价关系,所以5与它们分为一组既{2,3,4,5,6,8,10}
4.第三个奇数7,与上面的组中无法形成等价关系,所以7单独分为一组,既{2,3,4,5,6,8,10},{7} (思考,N至少等于多少的时候,7才会出现等价关系?没错,当2x7=14,既N==14开始,7才有等价关系)
5.第四个奇数9,与上面的组中的3,6有共同因子3,能形成等价关系,所以最终形成的组为{2,3,4,5,6,8,9,10},{7},共两组
我们发现4个奇数中,前两个都能与偶数组中的某个偶数形成等价关系,从而进入偶数组里面,而第三个奇数7,却需要N至少等于14开始,既与2和14形成等价关系,才能进入偶数组里面。但第四个奇数9却能与前面出现过的数有共同因子(3和6),却不用N至少等于18开始这个条件。
仔细观察,我们发现,3、5、7其实都是素数,一个素数如果要找到跟它有共同因子的数,我们只能从这个素数后面开始找(往前找是找不到一个数跟它有共同因子的(1不算)),而离这个素数最近的根它有共同因子的数就是2x这个素数,例如2x7=14。 而不是素数的9,一定与9前面的某一个数有共同因子,所以9可以进入前面已形成的组里。
加深理解:N==100;
最终形成的组为{2,3,4,5,6,7......49,50,51,52,54,55,...99,100} {53} {59} {61} {67} {71} {73} {79} {83} {89} {97}
在2~100中,2~50内的数乘以2后都能在4~100内中找到, 50后面的数除了素数以外都能与前面有共同因子,而50后面的数中的素数由于没有共同因子,所以各自成为一组。
总结:
因为:
在2~N中,2~(N/2)的数乘以2其实都能在4~N都找到,而N/2~N除了素数以外的数,都能与前面的某一个数有共同因子,所以它们将组成一个组(因为等价关系) 。
所以:
2~N/2与N/2~N的非素数将成为一组 N/2~N中的素数各自成为一组
坤坤的任务:是找出最后形成的组的总数(sum)。
关键就是求N/2~N之间到底有多少个素数?
关键代码:
#include <iostream>
using namespace std;
const int MAX=10000001;
int fac[MAX]={0},a[MAX]={0},num=0; //num记录有多少个素数
void ai(){ //埃式筛法
for(int i=2;i<MAX;++i){
if(fac[i]==0)
for(int j=i;j<MAX;j+=i)
fac[j]=i;
if(fac[i]==i) ++num; //如果(fac[i]==i),则说明i是素数,更新num
a[i]=num; //a[i]存储从2到i之间有多少个素数
} //a[2]=1,a[3]=2,a[4]=2,a[5]=3,a[6]=3,a[7]=4
}
// a[6]-a[3]=3-2=1,代表着:2到6之间的素数(2,3,5)-2到3之间的素数(2,3)=3到6之间的素数(5)
int main()
{
ai(); //初始化
int T,N,sum; //sum为总组数
cin>>T;
while(T--){ //T组测试用例
cin>>N;
sum=1; //2~N/2的数和N/2~N的非素数为一组
//if(N/2<2) --sum; N==2||N==3的情况sum会多算一次 ,虽然测试用例没有
sum+=a[N]-a[N/2]; //前缀和
cout<<sum<<endl;
}
return 0;
}