HDU 3208(容斥原理+高精度开根)
传送门:
题面:
LMY and YY are number theory lovers. They like to find and solve some interesting number theory problems together. One day, they become interested in some special numbers, which can be expressed as powers of smaller numbers.
For example, 9=3^2, 64=2^6, 1000=10^3 …
For a given positive integer y, if we can find a largest integer k and a smallest positive integer x, such that x^k=y, then the power of y is regarded as k.
It is very easy to find the power of an integer. For example:
The power of 9 is 2.
The power of 64 is 6.
The power of 1000 is 3.
The power of 99 is 1.
The power of 1 does not exist.
But YY wants to calculate the sum of the power of the integers from a to b. It seems not easy. Can you help him?
Input
The input consists of multiple test cases.
For each test case, there is one line containing two integers a and b. (2<=a<=b<=10^18)
End of input is indicated by a line containing two zeros.
Output
For each test case, output the sum of the power of the integers from a to b.
Sample Input
2 10 248832 248832 0 0
Sample Output
13 5
题目描述:
在一个区间[l,r],其中每一个数必定可以写成写成a^p的形式,现在设p的和为res,问你在[l,r]区间内最大的res为多少。
题目分析:
个人感觉这个题跟hdu2204很类似,只不过这个题要做的是一段区间的值。
首先,不难发现,对于一段区间而言,区间某个数的幂的个数存在着累加性。即倘若我们需要求区间[l,r]的结果,我们只需要先求出区间[1,r]的结果res1,再求出区间[1,l-1]的结果res2,之后用res1-res2即为区间[l,r]的结果。
之后,题目就又喜闻乐见的转化为求1到n区间中,能够写成形式的数的个数的问题。显然,1到n区间中最多有个数是能够写成的形式的,因此我们只需要枚举幂数k即可。
因为题目中n最大为1e18,而又有,因此幂的上限最大为64,故我们只需要将k枚举到63位即可。
但是,这么做显然是会重复计数的,如因此我们还需要把重复的部分根据容斥原理剪掉。因为题目中要求我们使得结果最大,因此我们需要尽可能的让幂数大的保留下来,也就是说,幂数较小的需要排斥掉。因此我们可以考虑最后从大到小枚举幂数,如果发现两个幂数(i%j==0且i>j)我们就把j中j和i相同的部分抹去。
但是这个题有个相当不愉快的一点,在这个题中,pow的精度误差相当的大,因此我们需要手动用类二分的思想对提升pow的精度。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll sum[80];
const double eps=1e-18;
const ll inf = (ll)(1e18) + 500;
const ll INF = (ll)1 << 31;
ll multi(ll a,ll b)//快速乘
{
ll ans=1;
while(b){
if(b&1){
double judge=1.0*inf/ans;
if(a>judge) return -1;
ans*=a;
}
b>>=1;
if(a>INF&&b>0) return -1;
a=a*a;
}
return ans;
}
ll Find(ll x,ll k)//手动扩大pow精度
{
ll r=(ll)pow(x,1.0/k);
ll t,p;
p=multi(r,k);
if(p==x) return r;
if(p>x||p==-1) r--;
else
{
t=multi(r+1,k);
if(t!=-1&&t<=x) r++;
}
return r;
}
ll solve(ll n){
sum[1]=n;
int maxx=0;
for(int i=2;i<64;i++){//枚举63位幂
sum[i]=Find(n,1ll*i);
sum[i]--;
if(sum[i]==0){
maxx=i;//记录达到的最大的幂是多少
break;
}
}
for(int i=maxx;i>=2;i--){//从大到小枚举幂
for(int j=1;j<i;j++){
if(i%j==0) sum[j]-=sum[i];
//如果两个幂不互素,则证明有重复,则我们将小的幂减去大的幂的影响
}
}
ll res=0;
for(int i=1;i<=maxx;i++){
res+=sum[i]*i;
}
return res;
}
int main()
{
ll a,b;
while(cin>>a>>b){
if(a==0) break;
ll ans=solve(b)-solve(a-1);
cout<<ans<<endl;
}
return 0;
}