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

链接:https://www.jisuanke.com/contest/1555?view=challenges

题意:一个数可以用a*b表示且a,b不是一个平方数,这个数有几种a*b的表达方式,则这个数的价值就为几,输入一个n求前缀和

思路:一个数要么是素数,要么是素数的倍数。(1例外答案就是1)(下面每个数的价值用sum[]表示)

 这里用类似素数筛的方法。预处理,每个数都遍历一遍。

用vis标记素数和合数(1为素数,0为合数)

首先,先明确一个条件,任何合数都能表示成一系列素数的积。
然后利用了每个合数必有一个最小素因子,每个合数仅被它的最小素因子筛去正好一次。所以为线性时间
代码中体现在:
if(i%prime[j]==0)break;
prime数组 中的素数是递增的,当 i 能整除 prime[j],那么 i*prime[j+1] 这个合数肯定被 prime[j] 乘以某个数筛掉。
因为i中含有prime[j], prime[j] 比 prime[j+1] 小。接下去的素数同理。所以不用筛下去了。(这样可以节约时间,时间复杂度也降为O(n))

在满足i%prme[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。

摘抄于http://www.mamicode.com/info-detail-662432.html

所以有两种情况:

1.当i为素数时,sum为2.

2.当处理合数时,i*prime(此时,i*prime的最小素因子就是prime)分两种情况

  a.   i%prime!=0(i不是prime的倍数)那么,sum[i*prime]=sum[i]*2;

因为,i可以用a*b表示,a,b都不是prime的倍数,i*prime就可以用(a*prime )*b,和a* (prime*b)表示(所以是两倍)

  b.  i%prime==0,又分两种情况:(break,就是上面讲的线性时间复杂度)

    一.i%(prime*prime)==0,则sum[i*prime]=0;因为i如果用a*b表示,a是prime的倍数,b也是prime平方的倍数。所以i*prime其中

有一因子一定是平方数。

    二.i%(prime*prime)!=0,则sum[i*prime]=sum[i]/2,因为sum[i*prime]=sum[i/prime]=sum[i]/2.

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e7+10;
ll ans[maxn];//前缀和 
int vis[maxn],prime[maxn],sum[maxn];

void init()
{
    memset(vis,0,sizeof(vis));
    memset(ans,0,sizeof(ans)); 
    int pos=0;//素数的个数 
    ans[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!vis[i])//标记素数 
        {
            prime[pos++]=i;
            sum[i]=2;
        }
        for(int j=0;j<pos&&i*prime[j]<maxn;j++)
        {
            vis[i*prime[j]]=1;//标记合数 
            if(i%prime[j])
                sum[i*prime[j]]=sum[i]*2; 
            else
            {
                if(i%(prime[j]*prime[j])==0)
                    sum[i*prime[j]]=0;
                else
                    sum[i*prime[j]]=sum[i]/2;
                break;//重点,当i%prime[j]时break,线性时间 
            }
        }
    }
    for(int i=2;i<maxn;i++)
        ans[i]=ans[i-1]+sum[i];
}

int main()
{
    init();
    int t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
        cout<<ans[n]<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiongtao/p/9682424.html