题目链接
题意
求小于等于 n n n的正整数中有多少个数刚好有四个约数,其中 n ≤ 1 0 11 n\leq 10^{11} n≤1011.
题解
显然不能直接筛.经过学习,我们发现
m
i
n
_
25
min\_25
min_25筛法可以在
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32)的复杂度内完成本题.
首先我们可以意识到成立的数字一定是某个质数的三次方或者两个不同质数的乘积,两者没有重合部分,可分开计算.
质数的三次方很好解决,筛出
n
\sqrt n
n的质数然后枚举到
n
n
n即可,关键是后者.
min_25筛可以用来解决类似
∑
i
=
1
n
{
i
∈
p
r
i
m
e
}
f
(
i
)
\sum_{i=1}^{n}\{i\in prime \}f(i)
∑i=1n{i∈prime}f(i)的问题,其中
f
(
i
)
f(i)
f(i)是一个多项式.
本题中需要计算
n
n
n以内质数的个数,故取
f
(
i
)
=
1
f(i)=1
f(i)=1.
令
g
(
j
,
m
)
g(j,m)
g(j,m)表示小于
m
m
m的质数或者最小的质因数大于
p
j
p_j
pj的贡献.
转移分为两种情况.
- p j 2 > m p_j^2>m pj2>m,这种情况下 p j 2 p_j^2 pj2是最小质因子为 p j p_j pj的最小数,因此合数不可能产生贡献,而只有质数可能再产生贡献,故 g ( j , m ) = g ( j − 1 , m ) g(j,m)=g(j-1,m) g(j,m)=g(j−1,m).
- p j 2 ≤ m p_j^2\leq m pj2≤m,此时需要减掉 p j p_j pj是最小质因子的贡献,本题中即为 g ( j , [ n p j ] ) − j + 1 g(j,[\frac{n}{p_j}])-j+1 g(j,[pjn])−j+1
然后滚动一下数组就可以了.
最后是对每一个
n
\sqrt n
n以内的质数
p
p
p求满足
p
×
q
≤
n
p\times q\leq n
p×q≤n的质数
q
q
q的个数,由于前缀和,我们可以轻松算出.
这个答案加上我们一开始算出来的质数立方的个数就是最后的答案了.
#include<bits/stdc++.h> //Ithea Myse Valgulious
/*省略输入头文件*/
using namespace std;
const int mulu=8e5;
typedef ll fuko[mulu|10];
fuko mp,mp1,mp2,g,pr,p;
ll min_25(ll n) {
ll i,j,m=sqrt(n+.5);
for (i=2;i<=mulu;++i) {
if (!p[i]) pr[++*pr]=i;
for (j=1;j<=*pr&&pr[j]*i<=mulu;++j) {
p[pr[j]*i]=1;
if (i%pr[j]==0) break;
}
}
for (i=1;i<=n;i=j+1) {
j=n/(n/i);
mp[++*mp]=n/i;
mp[*mp]<=m?mp1[mp[*mp]]=*mp:mp2[n/mp[*mp]]=*mp;
g[*mp]=mp[*mp]-1;
}
for (j=1;j<=*pr;++j) {
for (i=1;i<=*mp&&pr[j]*pr[j]<=mp[i];++i) {
ll tmp=mp[i]/pr[j];
tmp=tmp<=m?mp1[tmp]:mp2[n/tmp];
g[i]-=g[tmp]-j+1;
}
}
ll zw=0;
for (i=1;i<=*pr&&pr[i]<=m;++i) {
zw+=g[n/(n/pr[i])]-g[mp1[n/(n/pr[i])]];
}
for (i=1;i<=*pr&&pr[i]*pr[i]*pr[i]<=n;++i);
return zw+i-1;
}
int main() {
ll n;
read(n);
printf("%lld\n",min_25(n));
}
谢谢大家.