最小质因数和

这题实在是比较一棵赛艇就发上来好了。

题目:

给定n,求出小于等于n的所有合数的最小质因数之和。

对于70%的数据,n<=10^7。

对于100%的数据,n<=10^9。

题解:

70% 线筛大法好

100%

首先我们考虑对于每一个小于等于sqrt(n)的质数容斥,然后稍微推一推就可以得到一个比较靠谱的容斥方法,虽然复杂度玄学但是似乎跑得蛮快的。然后我们测一下时间……真是不巧n=10^9要跑2s左右。(我就是这么被卡掉的

标程是这样的:

我们定一个阀值k=100,对于<=k的质因数(一共也就才几十个)我们用科学的容斥搞一搞,这个复杂度基本没有。

对于>=k的质因数p我们可以发现n/p是在10^7以内的。然后为了保证质因数是最小的,我们必须只能选n不是<p质数的倍数的。那么我们发现n/p显然也要不是<p质数的倍数。

这样我们用一个暴力筛法来维护n/p,具体做法是因为p递增时n/p递减,那么我们考虑线筛的上界也是递减的,每次线筛赋值bool数组的时候顺便更新一下答案,减小上界的时候就把多的答案扣掉。既然1kw的暴力筛法可以过,这样显然是科学的。

n=10^9只要跑0.1s左右。事实上如果把k设成1000跑n=10^10也只要跑0.6s左右。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <vector>
#include <math.h>
#include <time.h>
#include <limits>
#include <set>
#include <map>
using namespace std;
int sq,n;
#define FJ 100 //1000?
#define ZS 10000005
bool yz[ZS+3];
int mn=ZS,cnt=0;
bool isprime(int x)
{
    for(int p=2;p*p<=x;p++)
    {
        if(x%p==0) return 0;
    }
    return 1;
}
int pn=0,ps[233333];
long long ans=0;
void dfs(int x,int lst,int dep)
{
    if(lst!=0)
    {
        ans+=n/x*(long long)ps[lst]*dep;
        if(x==ps[lst]) ans-=x;
    }
    for(int i=lst+1;i<=pn;i++)
    {
        if(ps[i]<=FJ&&(long long)x*ps[i]<=n) dfs(x*ps[i],i,-dep);
        else break;
    }
}
void xj(int p)
{
    while(mn>p) cnt-=yz[mn--];
}
void pj(int p)
{
    for(int j=p;j<=mn;j+=p)
    {
        if(yz[j]) continue;
        yz[j]=1; ++cnt;
    }
}
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
int main()
{
    //FO(prime)
    scanf("%d",&n);
    sq=sqrt(n)+1;
    int cc=0;
    for(int i=2;i<=sq;i++)
    {
        if(isprime(i)) ps[++pn]=i;
    }
    dfs(1,0,-1);
    for(int i=1;i<=pn;i++)
    {
        int cur=ps[i];
        if(cur>FJ)
        {
            xj(n/cur);
            ans+=(n/cur-cnt-1)*(long long)cur;
        }
        pj(cur);
    }
    printf("%lld\n",ans);
}

嗯今天闫神还立了一个flag,说不会求质数的答案。那我们就是要求n以内质数的和。

a那就没有筛的意义了。
如果ps[b]^2

丢链接跑 http://mathoverflow.net/questions/81443/fastest-algorithm-to-compute-the-sum-of-primes

时间: 2024-12-22 17:03:13

最小质因数和的相关文章

HDU 5428 [The Factor] 分解质因数

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5428 题目大意:给你若干个整数,让你输出这些数乘积的一个最小因子,并且这个因子至少有3个因子. 关键思想:分解质因数,我们要找的其实就是两个最小质因数的乘积.我的算法关键就是维护最小的两个质因数. 代码如下: #include <iostream> #include <cmath> #include <cstdio> #include <algorithm> u

分解质因数(线性筛)

https://ac.nowcoder.com/acm/contest/923/B 我真是个辣鸡,现在才知道线性筛, 思路:对于每个数字的阶乘,开一个一维数组记录每个数字出现的个数,先利用一维差分将每个数字的阶乘中出现都得数字累加个数,然后再利用线性筛中,已知每个数字的最小质因数是什么,在比较过程中,我们可以从大到小比较,如果发现对于同一个数字它的出现次数不同,我们可以把这个数字划分为两个数字的乘积形式(已知每个数字的最小质因数都是固定的),如果数字相同就不用管了,最后在遍历一遍看看是否每个位置

线性筛法

关于线性筛法 线性是指O(n)内筛掉所有合数,还有一种方法叫埃氏筛法,我先证明埃氏筛法效率低,也就是会有重复. 证明如下: 埃氏筛法的原理是找到一个素数后,它的1~n倍就会被筛掉,任何一个合数都可以被拆成一个质数*合数的形式,我们对每一个质数对应的可能的(合)数都枚举了,这就保证了所有可能的合数都被筛掉了.为什么不是最优呢?问题出在那个质数上,对于一个合数m,m=h*P,P是质数且P>m的最小质因数,那么m也可以表示为m=H*p,(H是个比h大的合数,p是m的最小质因数),这样我们在枚举p的倍数

O(N)的素数筛选法和欧拉函数

首先,在谈到素数筛选法时,先涉及几个小知识点. 1.一个数是否为质数的判定. 质数,只有1和其本身才是其约数,所以我们判定一个数是否为质数,只需要判定2~(N - 1)中是否存在其约数即可,此种方法的时间复杂度为O(N),随着N的增加,效率依然很慢.这里有个O()的方法:对于一个合数,其必用一个约数(除1外)小于等于其平方根(可用反证法证明),所以我们只需要判断2-之间的数即可. bool is_prime(int num) { const int border = sqrt(num); for

欧拉筛素数+求欧拉函数

线性筛法 prime记录素数,num_prime素数下标 它们保证每个合数只会被它的最小质因数筛去 a[0]=a[1]=1; for(int i=2;i<=n;i++) { if(!a[i]) prime[num_prime++]=i; for(int j=0;j<num_prime&&i*prime[j]<=n;j++) { a[i*prime[j]]=1; if(!(i%prime[j])) break; } } } 欧拉函数 是 积性函数:对于任意互质的整数a和b有

[日常训练]yayamao的神题

Description $yayamao$是数学神犇,一天他在纸上计算起了$1/P$, 我们知道按照模拟除法可以得到准确解,例如$1/7=0.(142857),1/10=0.1(0)$.$yayamao$发现无论他如何模拟小数都会出现循环,现在$yayamao$想知道循环的长度以及循环出现之前,小数点后面的未循环的数字的位数.例如$1/15=0.0(6)$,那么它的循环长度为$1$,小数点后面的未循环的数字的位数为$1$;$1/4=0.25(0)$,那么它的循环长度为$1$,小数点后面的未循环的

【数论算法基础理论与实现】

[题目太正式了我还怎么写ヾ|≧_≦|"] [很简要] [参考文献:<算法导论>.白书.天宇哥哥课件] 1.基础 [除法定理]:对于任何整数a和正整数n,存在唯一整数q和r,满足0<=r<n且a=qn+r   WARN:C++中貌似不完全遵守这个东西,n认为是|n|,并且a为负时r可以为负 []  2.最大公约数 几条性质:gcd(a,b)=gcd(|a|,|b|) gcd(a,0)=|a| gcd(a,ka)=|a|   gcd(a,b)=gcd(b,a mod b) -

【BZOJ】3309: DZY Loves Math 莫比乌斯反演优化

3309: DZY Loves Math Description 对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b). Input 第一行一个数T,表示询问数. 接下来T行,每行两个数a,b,表示一个询问. Output 对于每一个询问,输出一行一个非负整数作为回答. Sample In

C语言打印100 ——200之间的素数

根据素数定义,只能被1和它本身整除的自然数为素数.利用定义可以循环判断该数除以比它小的每一个自然数(不包括1),如果都不能整除,则这个数就是素数. 由于上述方法效率太低,对此方法进行优化.我们都知道偶数一定不是素数,那只求剩余一般的数.如果一个数是合数,那么它的最小质因数肯定小于等于他的平方根,由合数定理可进一步优化,只需要循环判断该数除以比它的平方根小的每一个自然数(大于1)即可. C语言代码如下: #include<stdio.h> #include<math.h> int m