这篇文章将讨论反素数,素数大家都知道是什么,那么什么又是反素数呢?
先看定义:对于任何一个整数n,将其约数的个数记为g(n),如g(1) = 1,g(6) = 4,并且如果对于任何一个整数i (0<i<n),都满足g(i) < g(n) ,那么我们称n为反素数。
再看性质:1、一个反素数的质因子必然是从2开始的连续质数。
2、如果反素数n = 2^a * 3^b * 5^c * 7^d * ......,那么必然a >= b >= c >= d。
最后看一下ACM中有关反素数的一般题型:
1、找出不大于n的最大反素数。
2、找出不大于n的约数个数最多的数(与1等价)。
3、找出一个约数个数为n的最小数。
解题思路:结合反素数的定义及性质,最基本的原理是以2、3、5、......建立搜索树,枚举每一个质因子,然后判断记录。
下面源代码中德AntiPrime[n]代表约数个数为n的最小数:
using namespace std;
typedef __int64 lld;
lld AntiPrime[1010];
lld prime[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
void GetAntiPrime(lld cur, int cnt, int limit, int k)
{
//cur:当前枚举到的数
//cnt:该数的因数个数
//limit:因数个数的上限,2^t1*3^t2*5^t3……t1>=t2>=t3……
//k:第k大的素数
if(cur>((lld)1<<60) || cnt>150) return;
if(AntiPrime[cnt]!=0 && AntiPrime[cnt]>cur) // 当前的因数个数已经记录过且当时记录的数比当前枚举到的数要大,则替换此因数个数下的枚举到的数
AntiPrime[cnt] = cur;
if(AntiPrime[cnt] == 0) // 此因数个数的数还没有出现过,则记录
AntiPrime[cnt] = cur;
lld temp = cur;
for(int i=1; i<=limit; i++) // 枚举数
{
temp = temp*prime[k];
if(temp > ((lld)1<<60)) return;
GetAntiPrime(temp, cnt*(i+1), i, k+1);
}
}
下面是一道相关的例题,HDOJ:4228,时空转移( 点击打开链接),题目如下:
Flooring Tiles
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 347 Accepted Submission(s): 137
Given an integer N, what is the smallest number of square tiles needed to be able to make exactly N unique rectangles, and no more, using all of the tiles? If N=2, the answer is 4.
2 16 19 0
4 840 786432
有一些相同的正方形,可以拼出n种不同的长方形,现问如果要拼出n种长方形最少需要的正方形个数。
分析:
可以感觉到,有可能有关整数n的因子,因为都是组成的关系嘛,再仔细一想,其实就是求约数个数为2*n或者2*n-1的最小整数。
源代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef __int64 lld;
lld AntiPrime[1010];
lld prime[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
void GetAntiPrime(lld cur, int cnt, int limit, int k)
{
if(cur>((lld)1<<60) || cnt>150) return;
if(AntiPrime[cnt]!=0 && AntiPrime[cnt]>cur)
AntiPrime[cnt] = cur;
if(AntiPrime[cnt] == 0)
AntiPrime[cnt] = cur;
lld temp = cur;
for(int i=1; i<=limit; i++)
{
temp = temp*prime[k];
if(temp > ((lld)1<<60)) return;
GetAntiPrime(temp, cnt*(i+1), i, k+1);
}
}
int main()
{
int n;
GetAntiPrime(1,1,75,0);
for(int i=1;i<=75;i++)
{
if(AntiPrime[i*2-1]!=0 && AntiPrime[i*2]!=0)
AntiPrime[i] = min(AntiPrime[i*2-1],AntiPrime[i*2]);
else if(AntiPrime[i*2]!=0)
AntiPrime[i] = AntiPrime[i*2];
else
AntiPrime[i] = AntiPrime[i*2-1];
}
while(~scanf("%d",&n) && n)
printf("%I64d\n", AntiPrime[n]);
return 0;
}
其它相关的题目还有,HDOJ:4133,4392,4542。ZOJ:1562,2562。CODEFORCE:27E。
接下来,我们将讨论欧拉函数,传送门(点击打开链接)。