Distinct powers (Project Euler 29 加强版)
题目大意:
$2<=a,b<=n$ 求 $a^b$能表示多少不同的正整数。
原题中n=100,可以直接暴力求解,常见的两种解法是写高精度或者取对数判断相等。 直觉告诉我应该有更加优秀的解法,于是翻了下discuss,找到了一种复杂度介于O(n)和O(nlognlogn)的解法,拿出来分享一下。
首先来看一个性质:
对于一个$a$,可以找到最小的$a_0$,使得$a=a_0^k$.
比如$8^4=4^6=2^{12}$ 都是等价的。
对于某个$a^b = (a_0^k)^{b}$, 它只可能和某些$a_0^{b_1}$,$(a_0^2)^{b_2}$,$(a_0^3)^{b_3}\ \cdots\ (a_0^{k-1})^{b_{k-1}}$ 等价。
我们来看 $a_0^i\ \ (1<=i<k)$ 与 $a_0^k$ 所能表示的那些数会重复。
显然$a_0^{lcm(i,k)}$ $a_0^{2*lcm(i,k)}$ $a_0^{3*lcm(i,k)\ \cdots}$这些都是可以同时被$a_0^i\ \ (1<=i<k)$ 与 $a_0^k$ 表示的。
对应到$a_0^k$的指数分别是 $\frac{lcm(i,k)}{k}$ $2*\frac{lcm(i,k)}{k}$ $3*\frac{lcm(i,k)}{k}\ \cdots$ 把这些指数用一个bool数组标记,最后就可以得到以$a_0^k$为基能表示多少个数。 而且这个值和$a_0$的值无关,只和k有关,记为cnt[k],所以可以预处理。
最后统计答案。 枚举$a_0(不能表示成另外一个数的幂的数)$把$a_0\ a_0^2\ a_0^3\ \cdots a_0^k$ 一起考虑,对答案的贡献就是cnt[1]+cnt[2]+...cnt[k].
具体实现看代码: 实测n=100w 本地运行只要0.2s左右。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 typedef long long ll; 8 #define N 1000001 9 #define M 21 10 11 ll cnt[N]; 12 bool flag[N]; 13 bool vis[M][N]; 14 15 int gcd(int x,int y) 16 { 17 int tmp; 18 while (y) 19 { 20 tmp=x%y; 21 x=y; y=tmp; 22 } 23 return x; 24 } 25 26 int lcm(int x,int y){return 1ll*x*y/gcd(x,y);} 27 28 int main() 29 { 30 //freopen("in.in","r",stdin); 31 //freopen("out.out","w",stdout); 32 33 int n,m=1; scanf("%d",&n); 34 for (int i=2;i<=n;i<<=1,m++); m--; 35 36 for (int i=2;i<=m;i++) 37 { 38 for (int j=1;j<i;j++) 39 { 40 int l=lcm(i,j),len1=l/j,len2=l/i; 41 for (int k=1;k*len1<=n;k++) vis[i][k*len2]=true; 42 } 43 } 44 cnt[1]=n-1; 45 for (int i=2;i<=m;i++) 46 { 47 cnt[i]=cnt[i-1]; 48 for (int j=2;j<=n;j++) if (!vis[i][j]) cnt[i]++; 49 } 50 51 ll ans=0; 52 for (int i=2;i<=n;i++) 53 { 54 if (flag[i]) continue; 55 int p=0; ll x=i; 56 do 57 { 58 flag[x]=true; 59 p++; x*=i; 60 }while (x<=n); 61 ans+=cnt[p]; 62 } 63 cout<<ans<<endl; 64 return 0; 65 }