[POI2002][HAOI2007]反素数

题目描述

  对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数。例如,整数1,2,4,6等都是反质数。现在给定一个数N,你能求出不超过N的最大的反质数么?

此题以前学习过,此次刷POI又一次碰到了,我们看到要找到不超过N的最大反质数,我们就可以将问题转化。

在一开始的时候我将问题转化错了,我将问题转化为找到不大于N的中约数最多的数中的的最大的那个。我们考虑它为什么它不对。

  令  ∀ x<y<=N 使g(x)=g(y)

    那么 那么我们发现对于y来说,当i=x的时候,不满足g(x)>g(i)的要求,若将题设改为找到不大于N的中约数最多的数中的的最小的那个就会满足要求

由公式n= a[1]^k[1] * a[2]^k[2] *…..a[n]^k[n],约数个数 t=(k[1]+1)(k[2]+1)…..*(k[n]+1)可得出约数个数计算的方法由此我们可以进行搜索。

这里对于我们的搜索有一个小剪枝(不加会TLE,实测有效):

  一个数由许多质数的某次方相乘得到,难么满足题设的数所分解的较大的质数的指数一定小于较小的质数的指数

证明也很简单:如果较大的质数的指数大于较小的质数的指数,那么我们将两个质数的指数交换会使最终乘起来的数字变小,却不会使该数的约数个数变少,至此原剪枝得证

实现如下:

#include <algorithm>
#include <iostream>
#include <cmath>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
const ll su[16]={1,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
inline ll read()
{
    register ll p(1),a(0);register char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if(ch=='-') p=-1,ch=getchar();
    while(ch>='0'&&ch<='9') a=a*10+ch-48,ch=getchar();
    return a*p;
}
inline ll min(ll x,ll y){return x>y?y:x;}
ll n,ans=0x3f3f3f3f,maxn=0,ke=0;
void dfs(ll xx,ll pre,ll num,ll jl)
{
    ll ji=0;
    if(num>maxn) ans=xx,maxn=num;
    else if(num==maxn) ans=min(ans,xx);
    if(pre==16) return;
    while(ji<=jl&&xx<=n)
    {
        dfs(xx,pre+1,num*(ji+1),ji);
        xx*=su[pre];++ji;
    }
}
int main()
{
    n=read();
    dfs(1,1,1,30);
    printf("%lld
",ans);
    return 0;
}