f(i):i能拆分成两个数的乘积,且要求这两个数中各自都没有出现超过1 f ( i ) : i 能 拆 分 成 两 个 数 的 乘 积 , 且 要 求 这 两 个 数 中 各 自 都 没 有 出 现 超 过 1 次的质因子。每次给出 n n ,求 ∑ni=1f(i) ∑ i = 1 n f ( i )
1≤n≤2e7 1 ≤ n ≤ 2 e 7 每次查询即使都是 O(n) O ( n ) ,也一定会超时,因此打表
首先我们发现一个数 n n 由两个数 ab a b 相乘得到,并且 ab a b 这两个数中没有出现两次的因子,根据算术基本定理,任何数可以有素数组成,说明数 n n 的相同素因子个数不能超过2
因此我们对素因子个数进行分类讨论便能发现规律
由于数据大打表必须使用欧拉筛,线性复杂度
在筛选过程中:
1.对于数 i是素数p i 是 素 数 p 的情况:一定只有两种,即 1×p和p×1 1 × p 和 p × 1 所以 f(p)=2 f ( p ) = 2
2.当数 i的因子中没有素数p的时候,即p∤i i 的 因 子 中 没 有 素 数 p 的 时 候 , 即 p ∤ i ,这个时候我们乘上p后相当于多了一个新的素因子
假设原来素因子有 p1 p 1
组合方式为
当新加入一个素数 p p 的时候, p p 可以在上面的的任意一边,即对于上面每一种方式,新加的p可以
使它变成两种,即放左边放右边
所以 f(i×p)=f(i)×2 f ( i × p ) = f ( i ) × 2
3.当数 i中已经有了素因子p的时候即p|i仍然需要分两种情况讨论: i 中 已 经 有 了 素 因 子 p 的 时 候 即 p | i 仍 然 需 要 分 两 种 情 况 讨 论 :
如果i中已经有一个p了,即单纯的 p|i p | i 此时再乘上一个p就成了 p2 p 2 ,p必须一边一个,相当于p不存在了不再对种类数产生影响,也就相当于少了一个素因子p,因此此时 f(i×p)=f(i)2 f ( i × p ) = f ( i ) 2
而如果已经两个p了,即 p2|i p 2 | i 此时 f(i×p)=0 f ( i × p ) = 0
code:
#include
using namespace std;
typedef long long ll;
const int maxn = 2e7+5;
bool vis[maxn];
int prime[maxn];
ll f[maxn];
ll res[maxn];
void init(){
int cnt = 0;
f[1] = 1;
for(int i = 2; i < maxn; i++){
if(!vis[i]){
prime[cnt++] = i;
f[i] = 2;
}
for(int j = 0; j < cnt && i * prime[j] < maxn; j++){
vis[i*prime[j]] = 1;
if(i % prime[j] == 0){
if(i % (prime[j] * prime[j]) == 0) f[i*prime[j]] = 0;
else f[i*prime[j]] = f[i] / 2;
break;
}
else{
f[i*prime[j]] = f[i] * 2;
}
}
}
for(int i = 1; i < maxn; i++){
res[i] = res[i-1] + f[i];
}
}
int main(){
init();
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
printf("%lld\n",res[n]);
}
return 0;
}