hdu 3208
容斥原理
引入:已知x=y^k,(x,y,k正整数),要使k最大,即y最小。
输入a,b;计算[a,b]中的数的k的和,(2<=a<=b<=10^18),可统计[1,b]和[1,a-1]中数k的和,再相减可得出答案。
因为2^62>10^18;即k<62;可以计算区间上1次方,2次方,3次方..的数的个数;其间会重复计算,j%i==0则会重复计算,需要减去
num[i]-=num[j];(注意:要从后往前减)..最后乘加起来就好了。。
这题坑爹的是计算次方个数时卡精度,因为pow(x,1.0/k)精度丢失了,最明显的是就是[2,1e18]时的9次方的个数为99,就是怎么发现的,
然后就考虑处理精度的问题,其实pow(x,1.0/k)算出的值上下波动不会超过1的。
只要计算{r-1,r,r+1}^k,哪个最靠近x就可以了。计算r+1时会爆64位的,需要在相乘上做判断。
AC代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<iostream> 5 #define ll __int64 6 #define inf (ll) 1e18+300 7 #define eps 1e-9 8 using namespace std; 9 ll why=(ll)1<<31; 10 ll num[100]; 11 ll quickpow(ll m,int n) 12 { 13 ll b=1;int k=n; 14 while (n>0) 15 { 16 if (n&1) 17 { 18 double judge=1.0*(inf)/b; 19 if (judge<m) return -1;//避免b*m爆64 20 b*=m; 21 } 22 n=n>>1; 23 if (m>why&&n>0) return -1;//避免m*m爆64 24 m=m*m; 25 } 26 return b; 27 } 28 ll find(ll x,int k) 29 { 30 ll l=1,r=(ll)pow(x,1.0/k); 31 ll tt,pp,qq; 32 pp=quickpow(r,k); 33 if (pp==x) return r; 34 if (pp>x||pp==-1) --r; 35 else 36 { 37 tt=quickpow(r+1,k); 38 if (tt!=-1&&tt<=x) ++r; 39 } 40 return r; 41 } 42 ll f(ll x) 43 { 44 int i,j,k; 45 ll ans=0; 46 if (x<=3) return x; 47 memset(num,0,sizeof(num)); 48 num[1]=x; 49 for (i=2;i<63;++i) 50 { 51 num[i]=find(x,i)-1; 52 if (!num[i]) break; 53 } 54 k=i; 55 for (i=k-1;i>0;--i) 56 { 57 for (j=1;j<i;++j) 58 if (i%j==0) num[j]-=num[i]; 59 } 60 ans=num[1]; 61 for (i=2;i<k;++i) 62 { 63 ans+=(i*num[i]); 64 } 65 return ans; 66 } 67 int main () 68 { 69 ll a,b; 70 while (scanf("%I64d%I64d",&a,&b)&&(a+b)) 71 { 72 printf("%I64d\n",f(b)-f(a-1)); 73 } 74 return 0; 75 } 76 /* 77 2 1000000000000000000 78 123 4564651321324813 79 12 132465786543132132 80 81 1000000001002087980 82 4564651389245135 83 132465786908165783 84 */