分解因式 jdoj1370-vijos1229
题目大意:一个自然数n,求出1~20000中第一个约数个数等于n的数,如果大于20000,则输出No Solution。
注释:n<=80.在此,我们令N=20000
想法:开始想暴力,一寻思好像没戏。因为如果是极限数据的话,我们首先枚举到20000就是O(N),然后对于每一个数将它质因数分解,时间复杂度就算用pollard_rho也是$O(N^{\frac{1}{4}})$,所以总时间复杂度是$O(N^{\frac{5}{4}})$,发现... ...哈哈能过啊,哈哈。咳咳,在此我们介绍更优秀的算法。
我们用快筛欧拉的思想,外层循环枚举2-N,然后对于当前最大的质因数prime[j],我们用epsilon[i]表示i的约数个数。那么epsilon[i*prime[j]]等于什么呢?很容易想到,因为prime[j]是素数,所以,当i%prime[j]不是0是,即gcd(i,prime[j])=1,所以,对于i的任意一个约数k来讲,k*prime[j]都是i*prime[j]的约数,而且,i*prime[j]的每一个约数,都可以分为i的约数或者i的约数乘以prime[j],这种情况我们就讨论完了。
那如果是prime[j]|i呢?我们考虑,在i*prime[j]中的什么样的约数是i中没有的,我们设$prime[j]^k||i$,那么,如果有一个数是$prime[j]^{k+1}$的倍数而且是i*prime[j]的约数,那么这个数就是i里不存在的。我们期望统计这样的数有多少个(||的意思是恰整除,$j^k|i$中k的最大值)。显然,如果一个数$m*prime[j]^{k+1}|i*prime[j]$,那么$m|(i/prime[j]^k)$。我们设now=$i/prime[j]^k$。那么,显然,epsilon[now]中的任意一个数乘以$prime[j]^{k+1}$都满足题意,我们只需要快筛的时候把i%prime[j]mod到底即可。
最后,附上丑陋的代码... ...
1 #include <iostream> 2 #include <cstdio> 3 #define N 20000 4 using namespace std; 5 int epsilon[N]; 6 int prime[N]; 7 int cnt; 8 bool v[N]; 9 int main() 10 { 11 epsilon[1]=1; 12 for(int i=2;i<=N;i++) 13 { 14 if(!v[i]) prime[++cnt]=i,epsilon[i]=2; 15 for(int j=1;j<=cnt&&i*prime[j]<=N;j++) 16 { 17 v[i*prime[j]]=1; 18 if(i%prime[j]==0)//这里就用到了上面所叙述的 19 { 20 int now=i; 21 while(now%prime[j]==0) now/=prime[j]; 22 epsilon[i*prime[j]]=epsilon[i]+epsilon[now];//这道水题的精华 23 } 24 else 25 epsilon[i*prime[j]]=epsilon[i]*2; 26 } 27 } 28 int n; 29 scanf("%d",&n); 30 bool flag=false; 31 for(int i=1;i<=N-10;i++) 32 { 33 if(epsilon[i]==n) 34 { 35 printf("%d\n",i); 36 flag=true; 37 break; 38 } 39 } 40 if(!flag) printf("NO SOLUTION\n"); 41 return 0; 42 }
小结:我们的想法的切入比较简单,论证上有一些小小的思考而已。