P1463 [POI2002][HAOI2007]反素数 题解

CSDN同步

原题链接

简要题意:

\(f_x\) 表示 \(x\) 的因数个数。求 \(\leq n\) 的最大的 \(x\) 使得 $f_x > f_y (1 \leq y < x) $.(即求一个最大的比它小的数因数都多的数)

本题将作为反素数的模板题。

其实这就是 反素数 的定义,求反素数。

算法一

注意到 \(f_x\) 是积性函数。所以我们用欧拉筛筛出来。

然后再暴力判断反素数。

时间复杂度:\(O(n)\).

实际得分:\(O(\texttt{rp})\).(看运气,因为这题没说部分分)

算法二

我们只需要求最大的反素数,不用求所有的。

比方说一个反素数 \(n = \prod_{i=1}^k {p_i}^{k_i}\) 为其标准分解形式,其中 \(p_i\) 严格递增。

\(n\) 的因数个数为 \(\prod_{i=1}^k (k_i + 1)\)

此时必然存在:\(k_i\) 不严格递减。

为什么呢?

比方说 \(12 = 2^2 \times 3\)\(18 = 2 \times 3^2\).

此时两者因数个数相同,所以 \(18\) 肯定不是反素数。

也就是说,大的素数个数一定不能超过小的,这样就可以是反素数了。

那么你会问了,这怎么枚举呢?

\(1\) 开始轮流乘上素数(注意剪枝),具体见代码。

因为 \(2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 \times 19 \times 23 \times 27 > 2 \times 10^9\),所以只要这些素数就够了。

时间复杂度:\(O(\text{wys})\).(难以分析,但是应该居于线性和对数之间,和开方差不多但难以证明)

实际得分:\(100pts\).

反素数算法太玄学

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n;
int p[20]={0,2,3,5,7,11,13,17,19,23,29};
ll ans=-1,maxn=-1;

inline void dfs(ll dep,int prime,int now,int zhi) {
//dep 是当前的数,prime 是当前可以选择的质数编号,now 是因数个数,zhi 是最大幂次
	if(now>maxn || (now==maxn && dep<ans)) ans=dep,maxn=now; //更新答案
	int j=0; ll i=dep; while(j<zhi) { //枚举幂次
		j++; if(n/i<p[prime]) break; //超出 n
		i*=p[prime]; if(i<=n) 
		dfs(i,prime+1,now*(j+1),j); //往后枚举
	}
}

int main(){
	n=read();
	dfs(1,1,1,30);
	printf("%lld\n",ans); //答案
	return 0;
}

posted @ 2020-03-31 15:31  bifanwen  阅读(184)  评论(0编辑  收藏  举报