不难分析出将整数n分解成质因数相乘 n=p1^a1+p2^a2+.....=pn^an * x
依据题目要求,可知,an直可取1或者2,当an为1时,一个素数有两种拆分方式,所以贡献为2,当an为2时,只有将一个pn分配到x里去,且x本身不可整除an时,才能满足条件,因此贡献为1。
f(n)=2*f(x) (an=1) f(n)=f(x) (an=2) f(n)=0 (an=3)
介绍两种做法:暴力,数论
1、暴力
分段打表法,可将复杂度强行降到根号N,即每段长度为根号2e7,即4462,然后每次查询的数字必定落在一段上或者节点上,若在某段中间,则累加上去,最终复杂度为(根号N)^2
贴个别人的:https://blog.youkuaiyun.com/Du_Mingm/article/details/82290297
2、欧拉筛法:
修改欧拉筛素数的板子即可,注意break语句的位置,他是保证复杂度为O(N)的关键
# include<iostream>
# define maxn 20000100
# define ll long long
# include<stdio.h>
using namespace std;
const ll N=2e7+3;
int prime[maxn];
bool judg[maxn];
int f[maxn];
void pre()
{
int cnt=0;
f[1]=1;
for(int i=2;i<=N;i++)
{
if(!judg[i])
{
prime[cnt++]=i;
f[i]=2;
}
for(int j=0;j<cnt&&(prime[j]*i)<N;j++)
{
int num=i*prime[j];
judg[num]=true;
if(i%prime[j])
{
f[num]=2*f[i];
}
else if(i%(prime[j]*prime[j])==0)
{
f[num]=0;
}
else
{
f[num]=f[i/prime[j]];
break;
}
}
}
}
int main()
{
ll ans;
int T,n;
pre();
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ans=0;
for(int i=1;i<=n;i++)
{
ans+=f[i];
}
printf("%lld%s",ans,T==0?"":"\n");
}
}