题目传送门: hdu3208
题意:
求区间[a, b]中每个数的最大指数和。
(2<=a<=b<=1018)
直接求不好求,可以将问题转化为求[1, n],f(b) - f(a-1) 即为答案;
260>1018
, 最大指数不会超过60, 开根算出每个指数对应的数目个数,因为有些数有不同表示方法,例如
26=43=82
, 数目会有重叠,可以看出如果指数i整除指数j,则num[i] 包含num[j], 减去即可;
最后注意精度问题,pow精度丢失严重,得专门处理。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL INF = 1e18 + 300;
LL num[63];
LL quick_pow(LL a, LL b)
{
LL ans = 1;
while(b)
{
if(b&1)
{
if(ans > 1.0*INF/a)
return -1;
ans *= a;
}
b >>= 1;
if(a > 1.0*INF/a && b > 0)
return -1;
a *= a;
}
return ans;
}
LL get(LL n, int k)
{
LL ans = pow(n, 1.0/k);
LL tmp = quick_pow(ans, k);
if(tmp == n)
return ans;
if(tmp > n || tmp == -1)
ans--;
else
{
tmp = quick_pow(ans+1, k);
if(tmp != -1 && tmp <= n)
ans++;
}
return ans;
}
LL sol(LL n)
{
if(n <= 3)
return n;
int k = 1;
num[1] = n;
while(num[k] > 0)
{
k++;
num[k] = get(n, k) - 1;
}
for(int i = k-1; i >= 1; i--)
{
for(int j = i<<1; j < k; j++)
{
if(j%i == 0)
num[i] -= num[j];
}
}
LL ans = 0;
for(int i = 1; i < k; i++)
ans += i * num[i];
return ans;
}
int main(int argc, char const *argv[])
{
LL a, b;
while(scanf("%lld%lld", &a, &b), a&&b)
{
LL ans = sol(b) - sol(a-1);
printf("%lld\n", ans);
}
return 0;
}