http://acm.hdu.edu.cn/showproblem.php?pid=4542
题意:当type = 0, 求约数有k个的最小整数,当type = 1, 求非约数有k个的最小整数。
首先考虑type = 0 的情况
反素数的定义:对于任何正整数,其约数个数记为
,例如
,如果某个正整数
满足:对任意的正整
数,都有
,那么称
为反素数。
从反素数的定义中可以看出两个性质:
(1)一个反素数的所有质因子必然是从2开始的连续若干个质数,因为反素数是保证约数个数为的这个数
尽量小
(2)同样的道理,如果,那么必有
反素数为什么是保证约数个数为x的这个数n尽量小?如果有n1>……>n2,f(n1)=……=f(n2)=k,那么根据定义,n2才可能是反素数。(可能不是的原因是f(i)>f(n))
如果反素数的质因子不是连续的,那么必然有一种约数个数为x的连续情况(可以看成是指数不变,底数全部向前挪),所得的n一定比当前的小,违反了保证约数个数为x的这个数n尽量小。
如果不是t1>=t2>=t3……这样的不上升序列,那么约数个数为x的这个数n还不是最小的,同样也违反了。
约数个数为x的最小数n,一定满足(1)(2),但反之也不一定成立。
所以反素数一定满足(1)(2),但反之不一定成立。
题目中type = 0的情况,是求一个最小的数使它的约数个数为k,那么就是在满足(1)(2)的数中找最小的。可以用dfs。
type = 1时 ip[i]本来是表示i的非约数个数 后来改成了非约数个数为i的最小数为ip[i]
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL __int64
#define N 50005
using namespace std;
const int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
const LL INF = (((LL)1)<<62)+1;
LL ans;
int ip[N], k;
void Init()
{
for(int i=1;i<N;i++) ip[i] = i;
for(int i=1;i<N;i++)
{
for(int j=i;j<N;j+=i) ip[j]--;
if(!ip[ip[i]]) ip[ip[i]] = i;
ip[i] = 0; } }
void dfs(LL dept, LL limit, LL tmp, LL num) //当前处理第dept个质因子,limit是根据规律在限制指数,tmp是当前已累乘的数,num是当前已经产生了多少个约数 约数个数是同一个质因子出现个数+1累乘
{
if (num == k && tmp < ans) ans = tmp;
for (int i = 1; i <= limit; i++)
{
if (ans / double(p[dept]) <= tmp || num * (i+1) > k) break; // 不能写成 ans < tmp*p[dept] 会爆LL
tmp *= p[dept];
if (k % (num * (i+1)) == 0)
dfs(dept+1, i, tmp, num * (i+1));
}
}
int main()
{
Init();
LL tt = 1, n;
scanf("%I64d", &n);
while (n--)
{
LL T;
scanf("%I64d%I64d", &T, &k);
if (T == 1) ans = ip[k];
else
{
ans = INF;
dfs(0, 62, 1, 1);
}
printf("Case %I64d: ",tt++);
if (ans == 0)
printf("Illegal\n");
else if (ans >= INF)
printf("INF\n");
else printf("%I64d\n", ans);
}
return 0;
}