题目链接:https://vjudge.net/problem/UVA-11752
题解:
1.首先变量必须用unsig long long定义。
2.可以分析得到,当指数为合数的时候,该值合法。
3.由于最大值为(2^64)-1,而最小的合数为4, 所以底数最大都不超过(2^16)。且指数最大不超过64。
那么就可以:
枚举底数,然后计算出在最大值范围内,可以取到的最大指数。当最大指数小于4时,可以退出循环了。
然后枚举指数,从4开始,当指数为合数时,该值合法。
4.时间复杂度:O((2^16)* 64) < O(1e7)
反思:
现场做的时候,发现了:底数为素数,指数为合数的组合是合法的,且不会出现重复。
也知道底数在1e5之内,但却没有发现指数最大都不超过64(原因没有以 2^64 为分析对象)。
当时的想法是:在1e5之内筛选出素数和合数,然后素数与合数组合。但是没想到怎么刹车以防超过最大值。
原来是:在组合之前,先计算出该素数下最大的指数,然后就以这个最大指数为刹车基准。
只恨当时没想到……
而且:不管底数是否为素数,直接枚举也不会超时。所以才导致想到了复杂的方法,这源于没有事先对复杂度作出准确的估计。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> #include <queue> #include <stack> #include <map> #include <string> #include <set> #define ms(a,b) memset((a),(b),sizeof((a))) using namespace std; typedef long long LL; const int INF = 2e9; const LL LNF = 9e18; const int mod = 1e9+7; const int maxn = 1e5+100; int vis[100], p[100], cnt = 0; typedef unsigned long long ull; void init() { int m = (64+0.5); for(int i = 2; i<=m; i++) if(!vis[i]) { p[++cnt] = i; for(int j = i*i; j<64; j += i) vis[j] = 1; } } set<ull>s; //用set完成了排序加去重的功能 int main() { init(); //标记在64以内的合数 ull maxx = (1LL<<64)-1; //最大值 for(ull i = 2;; i++) //枚举底数 { ull x = maxx; int t = 0; //尽量不要用取对数的方法求得t,因为常常出现浮点数的相关问题 while(x>=i) //计算i的最大次幂 { t++; x /= i; } if(t<4) break; x = 1; for(int j = 1; j<=t; j++) { x *= i; if(vis[j]) s.insert(x); } } s.insert(1); set<ull>::iterator it; for(it = s.begin(); it!=s.end(); it++) cout<<*it<<endl; }