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

本文解析了一道ACM-ICPC比赛中的数论题目,通过素数筛选和分类讨论的方法,实现了对特定条件下数的因子组合计数的高效算法。

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

题意:

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

分析:

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

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

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

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

在筛选过程中:

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

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

假设原来素因子有p1p1

组合方式为

1×p1        p1×11×p1        p1×1

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

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

3.当数ipp|ii中已经有了素因子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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值