题目大意
将一个数用唯一分解定理写成 x = 2^k1 * 3^k2 * 5 ^k3…
求1 ~n中x的gcd(k1, k2, k3)是1的个数。
input
4
4
2
72
10
output
2
1
61
6
idea
用总的个数,减去GCD是2, 3, 5, 7, …的,然后因为多减了6,10什么的,再加回来… 容斥一下即可。
gcd是2的数有2, 4, 8, 16, 32
gcd是3的数有3, 9, 27…
有多少个直接开根就可以了
但是这里存在一个很恶心的精度问题,需要把它乘回去验证一下,看代码吧
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;
ll h[maxn];
ll Get(ll n, ll last, int k) {
ll sum = 1;
for(ll i = last - 1; ; i++) { //从小一个开始乘,找到第一个大于n的数返回
sum = 1;
for(int j = 0; j < k; j++) {
sum *= i;
if(sum > n) break; //这里注意要及时break;
}
if(sum == n) return i;
if(sum > n) return i - 1;
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--) {
ll n;
scanf("%lld", &n);
ll num = n - 1;
for(int i = 0; i < 64; i++) h[i] = 1;
for(int i = 2; i < 64; i++) {
if(h[i]) {
double k = pow(n, 1.0 / i); //这里会有精度问题
ll last = floor(k); //将答案向上取整
last = Get(n, last, i); //验证答案是否正确
num -= h[i] * (last - 1); //减去1次方的
for(int j = i + i; j < 64; j += i) {
h[j] -= h[i];
}
}
}
printf("%lld\n", num);
}
}