P1221 最多因子数 题解

· · 题解

反素数

推荐和本蒟蒻博客一起食用

1.定义

"于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。 如果某个正整数x满足:g(x)>g(i),0<i<x,则称x为反质数。例如,整数1,2,4,6等都是反质数。"
                                                     ————度娘说的

什么意思呢?用自己的口水话解释一下:对于一个数 x,如果从1(x-1)这几个数中没有任意一个数的因数个数大于x的,那么就称x反素数。 其实,反素数的本质就是尽可能地让因子数量最大,但是数值最小。

2.性质

从反素数的定义可以发现凡是反素数都满足两个性质:

3.例题

大致思路是:从最小的质数(2)开始用Dfs枚举每个质数的指数,根据性质一,指数应是不上升的。每次Dfs传递四个值:

每次Dfs开始时传递的值是上一次枚举的结果,所以先保存值,再判定边界,最后循环枚举。

void Dfs(int deep,int arr,int cur,ll num){
    if(maxn<cur||(maxn==cur&&num<ans))
        maxn=cur,ans=num;
    if(deep>8)
        return;
    for(register int i=1;i<=arr;i++){
        num*=prime[deep];
        if(num>r)
            return;
        Dfs(deep+1,i,cur*(i+1),num);
    }
}

那边界又是什么呢?应该枚举多少个质数呢?(先去看看下面的完整代码吧) 我们注意到有关边界的有四处:

这。。。这不是暴力吗?? 要这个干嘛?

if(r-l<=100000){
        for(register ll i=l;i<=r;i++){
            cnt=0;
            for(register int j=1;j*j<=i;j++)
                if(i%j==0){
                    cnt++;
                    ll temp=i/j;
                    if(i%temp==0&&temp!=j)
                        cnt++;
                }
            if(cnt>maxn)
                ans=i,maxn=cnt;
        }
    }

首先,这个暴力的复杂度在一定范围内是可以接受的。问题就来了不是有Dfs了吗?还要暴力做什么? 我们思考这样一种毒瘤数据 情况,当L,R相距很近时,在这个区间中不存在反素数,但仍有答案。这种数据出现在枚举的区间很小情况下,便可以用暴力解决。至于区间多大时该用暴力,这个没有固定的值,只有根据题的范围来确定,不过我通常用100,000 or 200,000。

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
typedef long long ll;

template <typename T>
inline void read(T&x){
    x=0;int temp=getchar();bool f=false;
    while(temp<'0'||temp>'9'){if(temp=='-') f=true; temp=getchar();}
    while(temp>='0'&&temp<='9'){x=(x<<1)+(x<<3)+temp-'0'; temp=getchar();}
    if(f) x=-x;
}

ll prime[15]={2,3,5,7,11,13,17,19,23,29,31,37,41};
ll l,r;
ll maxn=0,ans,cnt;

void Dfs(int deep,int arr,int cur,ll num){
    if(maxn<cur||(maxn==cur&&num<ans))
        maxn=cur,ans=num;
    if(deep>8)
        return;
    for(register int i=1;i<=arr;i++){
        num*=prime[deep];
        if(num>r)
            return;
        Dfs(deep+1,i,cur*(i+1),num);
    }
}

int main(){
    read(l),read(r);
    if(r-l<=100000){
        for(register ll i=l;i<=r;i++){
            cnt=0;
            for(register int j=1;j*j<=i;j++)
                if(i%j==0){
                    cnt++;
                    ll temp=i/j;
                    if(i%temp==0&&temp!=j)
                        cnt++;
                }
            if(cnt>maxn)
                ans=i,maxn=cnt;
        }
    }
    else
        Dfs(0,31,1,1);
    printf("Between %lld and %lld, %lld has a maximum of %lld divisors.",l,r,ans,maxn);
    return 0;
}