ACM-ICPC 2018 南京赛区网络预赛 - J. Sum
题意:
f(i):i能拆分成两个数的乘积,且要求这两个数中各自都没有出现超过1f(i):i能拆分成两个数的乘积,且要求这两个数中各自都没有出现超过1次的质因子。每次给出nn,求
分析:
1≤n≤2e71≤n≤2e7每次查询即使都是O(n)O(n),也一定会超时,因此打表
首先我们发现一个数nn由两个数相乘得到,并且abab这两个数中没有出现两次的因子,根据算术基本定理,任何数可以有素数组成,说明数nn的相同素因子个数不能超过2
因此我们对素因子个数进行分类讨论便能发现规律
由于数据大打表必须使用欧拉筛,线性复杂度
在筛选过程中:
1.对于数的情况:一定只有两种,即1×p和p×11×p和p×1所以f(p)=2f(p)=2
2.当数i的因子中没有素数p的时候,即p∤ii的因子中没有素数p的时候,即p∤i,这个时候我们乘上p后相当于多了一个新的素因子
假设原来素因子有p1p1
组合方式为
当新加入一个素数pp的时候,可以在上面的的任意一边,即对于上面每一种方式,新加的p可以
使它变成两种,即放左边放右边
所以f(i×p)=f(i)×2f(i×p)=f(i)×2
3.当数i中已经有了素因子p的时候即p|i仍然需要分两种情况讨论:i中已经有了素因子p的时候即p|i仍然需要分两种情况讨论:
如果i中已经有一个p了,即单纯的p|ip|i此时再乘上一个p就成了p2p2,p必须一边一个,相当于p不存在了不再对种类数产生影响,也就相当于少了一个素因子p,因此此时f(i×p)=f(i)2f(i×p)=f(i)2
而如果已经两个p了,即p2|ip2|i此时f(i×p)=0f(i×p)=0
code:
#include <bits/stdc++.h>
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;
}