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\n",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,暴搜居然效率也很高。

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

posted @ 2017-07-13 17:18 Doggu 阅读(...) 评论(...) 编辑 收藏