ACM-ICPC 2018 南京赛区网络预赛 - J. Sum (找规律+打表)

ACM-ICPC 2018 南京赛区网络预赛 - J. Sum

题意:

f(i):i1 f ( i ) : i 能 拆 分 成 两 个 数 的 乘 积 , 且 要 求 这 两 个 数 中 各 自 都 没 有 出 现 超 过 1 次的质因子。每次给出 n n ,求 ni=1f(i) ∑ i = 1 n f ( i )

分析:

1n2e7 1 ≤ n ≤ 2 e 7 每次查询即使都是 O(n) O ( n ) ,也一定会超时,因此打表

首先我们发现一个数 n n 由两个数 ab a b 相乘得到,并且 ab a b 这两个数中没有出现两次的因子,根据算术基本定理,任何数可以有素数组成,说明数 n n 的相同素因子个数不能超过2

因此我们对素因子个数进行分类讨论便能发现规律

由于数据大打表必须使用欧拉筛,线性复杂度

在筛选过程中:

1.对于数 ip i 是 素 数 p 的情况:一定只有两种,即 1×pp×1 1 × p 和 p × 1 所以 f(p)=2 f ( p ) = 2

2.当数 ippi i 的 因 子 中 没 有 素 数 p 的 时 候 , 即 p ∤ i ,这个时候我们乘上p后相当于多了一个新的素因子

假设原来素因子有 p1 p 1

组合方式为

1×p1        p1×1 1 × p 1                 p 1 × 1

当新加入一个素数 p p 的时候, p p 可以在上面的的任意一边,即对于上面每一种方式,新加的p可以
使它变成两种,即放左边放右边

所以 f(i×p)=f(i)×2 f ( i × p ) = f ( i ) × 2

3.当数 ipp|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;
}

你可能感兴趣的:(数论)