题目链接:
HDU 2204 Eddy’s 爱好
题意;
给一个
n
,在
数据范围:
分析:
一开始我一直是从枚举
m
考虑,实在不知道怎么搞,耗时太多了。。。只能借助万能的网友。。。
我们可以枚举幂次
k ,考虑到 260>1018 ,最多只需要枚举到 60 幂次。
同时对于一个数 p 的幂次k 是个合数,那么 k 一定可以表示成k=r∗k′,其中k′是素数 的形式,那么:
p=mk=mr∗k′=(mr)k′
所以我们只需要枚举素幂次 k 即可。
同时如果pk≤n ,那么对于任意的 p′<p ,也一定满足 p′k≤n 。所以对于每个 k 我们令pk=n,即p=n1n ,求出最大的 p ,同时也就是满足pk≤n 的所有 p 的个数。
但是这样子会有重复。例如:k=2时,(22)3 和 k=3时,(23)2 就重复计数了(都是 26 )。这时候需要用容斥原理:加上奇数个素幂次相乘的个数,减去偶数个素幂次相乘的个数。又因为 2∗3∗5<60,2∗3∗5∗7>60 ,那么最多只要考虑三个素幂次相乘情况。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <climits> #include <cmath> #include <ctime> #include <cassert> #define IOS ios_base::sync_with_stdio(0); cin.tie(0); using namespace std; typedef long long ll; const ll prime[20] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59}; const int len = 17; const double eps = 1e-8; ll ans; double n; void dfs(int cur, int num, int total, ll k) { if(k > 60) return; //素因子连乘最多不能超过60次幂,因为2 ^ 60 > 10 ^ 18 if(num == total) { ll p = (ll)(pow(n, 1.0 / (0.0 + k)) + eps) - 1; //先把1去掉,eps精度误差 ans += p; return ; } if(cur == len) return ; dfs(cur + 1, num, total, k); //第i个素数不选 dfs(cur + 1, num + 1, total, k * prime[cur]); //第i个素数选择 } int main() { while(~scanf("%lf", &n)) { ll res = 0; for(int i = 1; i <= 3; ++i) { ans = 0; dfs(0, 0, i, 1); //从下标0开始,当前选择素数个数为0,需要选择素数个数i个,选择素数乘积为1 if(i & 1) res += ans; else res -= ans; } res += 1; //1在dfs时都没有统计 printf("%lld\n", res); } return 0; }