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内的所有反质数。挂个代码吧:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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,暴搜居然效率也很高。
但是,暴搜和我的方法时间差不多,而我却算出了所有的反质数。若要打表,显然更优。