1.问题描述
在一个神秘的世界里,坤坤正在玩一个古老的魔法游戏。这个游戏的目标是将一些神秘的咒语石头分组。每个咒语石头都有一个独特的编号,范围是从 22 到 N。在每一步,坤坤可以选择两个独特的咒语石头,如果他们共享一个大于 11 的共同因子,就可以将它们归入同一组。坤坤会一直将石头进行分组,直到不能再进行分组为止。
在这个游戏中,归属于一个组的石头形成了等价关系。这意味着如果石头 a 和石头 b 在同一组,石头 b 和石头 c 也在同一组,那么石头 a 和石头 c 就被认为是在同一组。
坤坤的任务是找出最后形成的组的总数。
输入格式
第一行将包含一个整数 T,表示测试案例的数量。然后是 T 行输入,每行包含一个整数 N。
数据范围保证:1≤T≤2×105,2≤N≤107。
输出格式
对于每个测试案例,输出一行,表示最后形成的组的总数。
样例输入
3
2
4
8
样例输出
2
2
3
说明
测试案例 1:最后的组是 { 22 }。
测试案例 2:最后的组是 { 2,42,4 } 和 { 33 }。
测试案例 3:最后的组是 { 2,3,4,6,82,3,4,6,8 },{ 55 } 和 { 77 }。
2.算法分析和描述
(1)素数筛法和前缀和计算:使用埃氏筛法(Sieve of Eratosthenes)找出每个数的最小质因子(即最小素因子)。这个步骤的目的是为了快速判断数的质因子,以便后续的因子关系判断。同时,计算一个前缀和数组 a[],其中 a[i] 表示从 2 到 i 之间有多少个素数。这个数组帮助在 O(1) 时间内得到任意范围内的素数数量。
(2)因子关系判断:对于每个数 N,我们需要找出满足条件的数对 (i, j),其中 i 和 j 都大于 11,并且它们有共同的大于 11 的因子。
这可以通过数的质因子信息和欧几里得算法来实现。具体地,我们可以预先计算每个数的所有因子,并根据其最小质因子来判断两个数是否有共同的大于 11 的因子。
(3)计算组数:利用前面预处理的数据,对于每个测试案例中的 N,通过遍历可能的数对 (i, j) 来计算出最终的组数。每对 (i, j) 如果满足条件,将它们归入同一组。
3.实现代码
#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;
}