• luoguP1463:反素数ant(打表心得☆)


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

    Step 1

    这个是在openjudge上(7591)能A的代码(原题:输出l~r的所有反素数),因为那时n<=2e7啊。

    当然也要讲一下原理。对于数的因子个数,不得不提唯一分解定理——n=a1^p1*a2^p2*…………其中a为该数的质因数,p为它的个数,比如49=7^2,其中a1=7,p1=2。于是因子个数为(p1+1)*(p2+1)*……(49有2+1=3个因子,1,7,49)。那么搜索的目的就很明显了,枚举质因子凑数字,凑出来的那一刻已经得到了它的因子个数!

    给质数打个表,打多少呢?前十几个质数虽然都很小,但乘起来肥肠肥肠恐怖啊(不信你自己试一试),所以后面都不用了。

    继续剪枝,举个栗子,2^3*3^2=72,2^2*3^3=108,它们的因子个数都为(2+1)*(3+1)=12,72明显小于108,也很明显如果把3的次方给2匀一个答案更优。同样的道理,2*3=6 < 2*5=10,如果质因数的组合不连续则一定存在更小的数比当前更优。

    最后我们画一棵解答树,第一层是2^1、2^2、2^3……它们的分支都有3^1、3^2、3^3……之后还有5、7、11等等接着找(具体参考程序,id为第几个质因数,now是数的大小,tot是因子数)。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int maxn,L,R,f,ans[20000010],p[]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
    void dfs(int id,int now,int tot)
    {
        ans[now]=tot;
        for(int i=1;now*p[id]<=R;++i) dfs(id+1,now*=p[id],tot*(i+1));
    }
    int main()
    {
        scanf("%d%d",&L,&R);
        dfs(1,1,1);
        for(int i=1;i<L;++i) maxn=max(maxn,ans[i]);
        for(int i=L;i<=R;++i)
            if(ans[i]>maxn){
                maxn=ans[i];
                f?printf(","):f=1;
                printf("%d",i);
            }
        if(!f) puts("NO");
        return 0;
    }

    Step 2

    如果能做到第一步,你就已经有一个不错的爆搜程序了,但对于2e9的范围来说还是弱了不少。仔细读题,发现这两道题还是有点区别的,我们不必求出这个范围内的所有反素数,只用找到那个最大的。既然这样,那我们就直奔答案寻找新的优化。更新条件有两个注意不要漏(估计只有像我这样头不好的人才会两次都写错……),之后参考Step 1的剪枝,我们尽量让小质数的次方数大,这也就意味着对于2^p1*3^p2*5^p3,满足p3<=p2<=p1。开个use数组记录一下p就好了。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<cstring>
     7 #define ll long long
     8 #define inf 1<<29
     9 using namespace std;
    10 int n,p[]={0,2,3,5,7,11,13,17,19,23,29,31},use[20];
    11 ll maxt,ans;
    12 void dfs(ll id,ll now,ll tot)
    13 {
    14     if(tot>maxt||(tot==maxt&&now<ans)) ans=now,maxt=tot;
    15     use[id]=0;
    16     while(now*p[id]<=n&&use[id]+1<=use[id-1]){
    17         use[id]++;
    18         now*=p[id];
    19         dfs(id+1,now,tot*(use[id]+1));
    20     }
    21 }
    22 int main()
    23 {
    24     scanf("%d",&n);
    25     use[0]=1<<29;
    26     dfs(1,1,1);
    27     printf("%lld",ans);
    28     return 0;
    29 }

    Step 3

    用我之前的程序可以打出比较小的表(2e8以内),观察一下,发现反素数其实很少,而且越往后它们的间隔越大(147026880~183783600,△=3e7+)。这也就意味着我们不用一个一个数去枚举小于它的最大的反质数。于是,先记录2e9的答案为1396755360,再把它减一输入程序,不断重复该操作与小的表接起来。我们终于打出最后的表了。(不容易啊QAQ~~~~~)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define ll long long
     4 using namespace std;
     5 int n,biao[]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,554400,665280,720720,1081080,1441440,2162160,2882880,3603600,4324320,6486480,7207200,8648640,10810800,14414400,17297280,21621600,32432400,36756720,43243200,61261200,73513440,110270160,122522400,147026880,183783600,245044800,294053760,367567200,551350800,698377680,735134400,1102701600,1396755360};
     6 int main()
     7 {
     8     scanf("%d",&n);
     9     for(int i=0;i<68;++i)
    10         if(biao[i]>n){
    11             printf("%d",biao[i-1]);
    12             return 0;
    13         }
    14     printf("%d",biao[67]);
    15     return 0;
    16 }
  • 相关阅读:
    如何紧急恢复SQL Server主数据库
    合理利用SQL Server查询执行计划
    理解RAID的四种级别
    sp_spaceused 显示行数、保留的磁盘空间以及当前数据库中的表、索引视图
    修复数据库索引问题:理解填充因数设置
    SQL Server调整因子
    sqlservr 命令行启动
    DBA需要考虑备份相关问题
    BCP
    “tablediff ”命令行工具
  • 原文地址:https://www.cnblogs.com/12mango/p/7592925.html
Copyright © 2020-2023  润新知