刚开始学习数论,加油。
历经几个小时,终于过了这道题,感动。
大致思路是先欧拉函数打表,然后二分查找。
这里先介绍lower_bound()函数:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的。
头文件必须包含<algorithm>。使用举例:数组a[]={1,3,4,8},lower_bound(a,a+2,2)-a的返回值就是1(需要注意的是这里必须减去a),返回值1也就是数组中3的下标;那么lower_bound(a,a+4,9)-a的返回值应该是什么?是4,这是因为如果所有元素都小于val,则返回last的位置。
本题的一个小坑就是虽然单个的欧拉函数值都是在int的范围内,但是当数据足够多(接近10000)足够大(接近或大于1000000)时,是有可能超出int的范围的,因此变量s(最终的结果)应为范围更大的数据类型。因为这个WA了好多次,改成long long就过了,心累。
附上完整代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#define N1 2000000+10
using namespace std;
int eular[N1];
void makelist(){//创建欧拉函数表
for(int i=1;i<N1;i++)
eular[i]=i;
for(int i=2;i<N1;i+=2){
eular[i]/=2;
}
for(int i=3;i<N1;i+=2){
if(eular[i]==i)
for(int j=i;j<N1;j+=i){
eular[j]=eular[j]/i*(i-1);
}
}
}
int main(){
int t,a,b=1;
makelist();
for(int i=2;i<N1;i++)
eular[i]=max(eular[i],eular[i-1]);//表中后面的项可能小于前面的项,而这些项在本题中都是没有意义的,故将之扩大,便于二分查找
scanf("%d",&t);
while(t--){
int n;
long long s=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a);
s+=lower_bound(eular+2,eular+N1,a)-eular;
}
cout<<"Case "<<b++<<": "<<s<<" Xukha"<<endl;
}
return 0;
}
此外,看到一个比较牛的性质是:
给你一个数t,让你求使得phi(n)>=t的最小的n,这个n就是大于t的最小的素数。按这个性质的话就可以打个素数表解决这道题,不过本人比较懒,就没有去做啦。
最后,总结就是审题一定要仔细,各方面都要考虑到,不能想当然!(第一篇博文,如有不对之处,欢迎指出。)