• BZOJ 1053 [HAOI2007]反素数ant


    53: [HAOI2007]反素数ant

    Description

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

    Input

      一个数N(1<=N<=2,000,000,000)。

    Output

      不超过N的最大的反质数。

    Sample Input

    1000

    Sample Output

    840
      当我见到这道题的时候是很开心的,这不就是在东北育才当时我打表过70分的divisorful数吗?细细一想,会发现:
      一个合法的divisorful数x要求:0<i<x间存在至多一个数,其约数个数比x多。
       而一个合法的反质数x要求:0<i<x间不存在任何一个数,其约数个数比x多。
      我们可以想,对于一个divisorful数,一定是小因子占先。
      例如,6=2*3,约数个数为4。然而,437=19*23,也是4个。很明显不是。
      而这种题,如果打表,会发现整个divisorful数集其实极其稀疏。那么,我们就可以逐步加入各质因数p,让原divisorful数集中的数*p,*p^2,*p^3,*p^4……直到查询的边界。然后将其排序,去掉不合法的数。如果数集没有变化,就结束了。
      我们总结一下,这样的题满足:
    • 可以使用数集表示,数集本身较稀疏
    • 可以按合理的顺序逐步加入元素,并可贪心化break
    • 加入新元素时可以较快建立其维护信息
      啊啊啊啊啊!这样可以很快的求出2000000000内的所有反质数。挂个代码吧:
     1 /**************************************************************
     2     Problem: 1053
     3     User: Doggu
     4     Language: C++
     5     Result: Accepted
     6     Time:40 ms
     7     Memory:852 kb
     8 ****************************************************************/
     9  
    10 #include <cstdio>
    11 #include <algorithm>
    12 #include <vector>
    13 const long long MAX = 2500000000ll;
    14 typedef std::vector< std::pair<long long,int> > SET;
    15 SET v, w;
    16 int p, i, j, k;
    17 SET expand(SET &v,int p) {
    18     SET w=v;
    19     for( i = 0; i < v.size(); i++ ) {
    20         long long x=v[i].first*p;
    21         for( j = 1; x < MAX; x*=p,j++ ) w.push_back(std::make_pair(x,v[i].second*(j+1)));
    22     }
    23     sort(w.begin(),w.end());
    24     SET ans;
    25     int mx=-1;
    26     for( i = 0; i < w.size(); i++ ) {
    27         int c=w[i].second;
    28         if(c<=mx) continue;
    29         ans.push_back(w[i]);
    30         mx=w[i].second;
    31     }
    32     return ans;
    33 }
    34 int main() {
    35     v.push_back(std::make_pair(1,1));
    36     for( p = 2; ; p++ ) {
    37         bool pri=1;
    38         for( i = 2; i * i <= p; i++ ) if(p%i==0) {pri=0;break;}
    39         if(!pri) continue;
    40         w=expand(v,p);
    41         if(w==v) break;
    42         v=w;
    43     }
    44     scanf("%lld",&k);
    45     for( i = 0; i < v.size(); i++ ) if(v[i].first<=k&&v[i+1].first>k) {printf("%lld
    ",v[i].first);break;}
    46     return 0;
    47 }
    数集公理化构造

      然而,我发现其他人根本没有这样想……

      hzwer如是说:

      本题似乎要先知道许多结论,不要问我证明。。

      一个数约数个数=所有素因子的次数+1的乘积

      举个例子就是48 = 2 ^ 4 * 3 ^ 1,所以它有(4 + 1) * (1 + 1) = 10个约数

      然后可以通过计算得一个2000000000以内的数字不会有超过12个素因子

      并且小素因子多一定比大素因子多要优

      预处理出前12个素数直接爆搜即可

      霎时间感觉自己被打脸。所谓的结论是:不超过N的最大的反质数是1~N间约数最多且相对最小的值x。很明显,这样的数x是一个反质数。若1~N间存在着更大的反质数,则显然月数个数比x多,与x约数最多矛盾。

      于是只需找1~N间约数最多且相对最小的值x,暴搜居然效率也很高。

      但是,暴搜和我的方法时间差不多,而我却算出了所有的反质数。若要打表,显然更优。 

  • 相关阅读:
    从Github上将laravel项目拉到新开发环境
    Nginx-Primary script unknown的报错的解决方法
    CentOS 7 安装PHP7+Nginx+Mysql5.7开发环境
    程序员面试经常会被问到的12个问题
    IOC(控制反转)的理解
    用冒泡排序的方法将数组从小到大排列
    常用设计模式详解
    PHP常见面试题总结
    能够遍历一个文件夹下的所有文件和子文件夹的函数
    线特征---LineMatching原理(四)
  • 原文地址:https://www.cnblogs.com/Doggu/p/bzoj1053.html
Copyright © 2020-2023  润新知