SP530 DIV2 - Divisors 2
题意简述
设 \(d(n)\) 为 \(n\) 的因数个数。若 \(d(n) > 3\),且对于所有的 \(m\ |\ n\),都有 \(d(m)\ |\ d(n)\),则称 \(n\) 为特殊的数。
求 \([1,10^6]\) 内所有的特殊的数。为减小输出量,只需在每 \(108\) 个特殊的数中输出第 \(108\) 个即可。
题目分析
设 \(n = p_1^{\alpha_1} \times p_2^{\alpha_2} \times \cdots \times p_{k}^{\alpha_k}(\alpha_1,\alpha_2,\cdots,\alpha_k \geq 1)\),则 \(d(n) = (\alpha_1+1)(\alpha_2+1) \cdots (\alpha_k+1)\)
根据题意,若 \(n\) 为特殊的数,则对于所有的 \(m\ |\ n\),都有 \(d(m)\ |\ d(n)\)
设 \(m = p_1^{\beta_1} \times p_2^{\beta_2} \times \cdots \times p_{k}^{\beta_k}(\beta_i \leq \alpha_i,i \in [1,k])\),则 \(d(m) = (\beta_1+1)(\beta_2+1) \cdots (\beta_k+1)\)
所以 \(d(m)\ |\ d(n) \Leftrightarrow (\beta_1+1) \cdots (\beta_k+1)\ |\ (\alpha_1+1) \cdots (\alpha_k+1)\)
不妨令 \(\beta_1 = \alpha_1,\beta_2 = \alpha_2,\cdots,\beta_{k-1} = \alpha_{k-1},\beta_k = \alpha_k-1\)
所以 \((\alpha_1+1) \cdots (\alpha_{k-1}+1)\alpha_k\ |\ (\alpha_1+1) \cdots (\alpha_{k-1}+1)(\alpha_k+1)\)
\(\Rightarrow \alpha_k\ |\ \alpha_k+1\)
\(\Rightarrow \alpha_k\ |\ 1\)
\(\Rightarrow \alpha_k = 1\)
神奇的事情发生了!我们将特定的 \(m\) 代到关系式,最终可以得到 \(\alpha_k = 1\)
同理,可以得到 \(\alpha_i = 1(i \in [1,k])\)
所以 \(n = p_1 \times p_2 \times \cdots \times p_k\)(\(p_i\) 为质数),\(d(n) = 2^k\)
又因为 \(d(n) > 3\),所以 \(k \geq 2\),即 \(n\) 不能为质数。
容易知道,这种形式的 \(n\) 肯定是符合要求的。
所以我们要找 \([1,10^6]\) 内不为质数,且分解质因数后所有质因子的次数均为 \(1\) 的数。
怎么找呢?从反面考虑,可以把所有质数以及能被质数平方整除的数排除掉。
对于质数,我们直接用线性筛。
对于能被质数平方整除的数,我们将目光放到线性筛的几行代码:
ck[i*pri[j]]=1;
if(i%pri[j]==0) break;
首先 i * pri[j] 这个数肯定能被 pri[j] 整除,那如果 i % pri[j] == 0 呢?说明 i 这个数肯定也能被 pri[j] 整除,所以 i * pri[j] 能被 pri[j] * pri[j] 整除,它就不是一个特殊的数,把它标记一下就行了。于是就有了下面的代码:
ck[i*pri[j]]=1;//ck是标记质数的数组
if(i%pri[j]==0) pd[i*pri[j]]=1;//pd是标记特殊的数的数组
去掉 break 是因为其他数可能会在这个循环内被标记,但是在其他循环内不被标记,与判断合数不同(自己想一想就知道了)。
代码较丑,勿喷
代码
#include<bits/stdc++.h>
using namespace std;
namespace my_std{
#define ll long long
#define bl bool
#define pc putchar
#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
#define enter pc('\n')
#define il inline
il ll read(){
ll sum=0,f=1;
char ch=0;
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
sum=sum*10+(ch^48);
ch=getchar();
}
return sum*f;
}
il void write(ll x){
if(x<0){
x=-x;
pc('-');
}
if(x>9) write(x/10);
pc(x%10+'0');
}
il void writeln(ll x){
write(x);
enter;
}
}
using namespace my_std;
ll pri[1000010],cnt=0,ans[1000010],tot=0;
bl ck[1000010],pd[1000010];//ck是标记质数的数组,pd为标记特殊的数的数组,0为是,1为不是
int main(){
pd[1]=1;//1肯定不是特殊的数,要去掉
fr(i,2,1e6){
if(!ck[i]){
pri[++cnt]=i;
pd[i]=1;//把质数去掉
}
fr(j,1,cnt){
if(i*pri[j]>1e6) break;
ck[i*pri[j]]=1;
if(i%pri[j]==0) pd[i*pri[j]]=1;//如上面解释
}
}
fr(i,1,1e6) if(!pd[i]) ans[++tot]=i;
for(ll i=108;i<=tot;i+=108) writeln(ans[i]);
}