我是靠谱客的博主 眼睛大柜子,最近开发中收集的这篇文章主要介绍【ALGO】数论算法(2) 质数筛例题前文链接例题,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章导航

  • 前文链接
  • 例题
    • POJ 2262. Goldbach's Conjecture
      • 题目大意
      • 解析
      • AC代码
    • CodeForces 776B. Sherlock and his girlfriend
      • 题目大意
      • 解析
      • AC代码
    • ACW 196/POJ 2689. 质数距离
      • 题目大意
      • 解析
      • AC代码
    • ACW 197. 阶乘分解
      • 题目大意
      • 解析
      • AC代码

前文链接

数学基础(1)

例题

POJ 2262. Goldbach’s Conjecture

题目链接

题目大意

验证 1 e 6 1e6 1e6之内的数满足Goldbach’s Conjecturre

Goldbach’s Conjecture(哥德巴赫猜想): 任何大于4的整数都可以分拆为两个奇质数之和

解析

看做 n n n次询问问题处理,先预处理出 1 e 6 1e6 1e6内的质数,从3开始找到vis[n-a]==false的质数. 算法时间复杂度为 O ( N + n ln ⁡ n ) mathcal{O}(N+frac{n}{ln n}) O(N+lnnn).

1 ∼ n 1sim n 1n之间的质数数量约为 n ln ⁡ n frac{n}{ln n} lnnn

AC代码

#include <cstdio>
#include <cstring>

using namespace std;
const int N=1000005;
int n, primes[N], cnt=0;
bool vis[N];

void Lsieve(){
    memset(primes, 0x00, sizeof primes);
    memset(vis, 0x00, sizeof vis);
    for(int i=2; i<N; ++i){
        if(!vis[i]) primes[cnt++]=i;
        for(int j=0; primes[j]<=N/i; j++){
            vis[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}

int main(){
    Lsieve();
    while(scanf("%d", &n), n){
        int i=1;
        for(; i<cnt; ++i){
            int a=primes[i];
            if(!vis[n-a]) {printf("%d = %d + %dn", n, a, n-a); break;}
        }
        if(i==cnt) printf("Goldbach's conjecture is wrong.n");
    }   
    return 0;
}

CodeForces 776B. Sherlock and his girlfriend

题目链接

题目大意

给定一个序列 2 , 3 , … , n + 1 2,3,dots,n+1 2,3,,n+1,对这些点进行染色,如果点 a a a是点 b b b的质因子,那么 a a a b b b需要被染成不同颜色,求出完成这个要求需要最少的颜色数,以及输出一种可行的染色方案.

解析

可以证明,至多使用 2 2 2种颜色可以完成染色需求,将序列 2 ∼ n + 1 2sim n+1 2n+1的数中质数分为一类,合数分为一类,将质数类染成红色,合数类染成蓝色即可完成需求.
考虑可以只用1种颜色即可完成需求的情况,显然如果序列中的数两两互质,则使用1种颜色即可完成需求,此时如果向序列中添加一个合数,使得其中一个质数成为该合数的质因子,那么就需要2种颜色. 因此,当 n ≤ 2 nleq 2 n2时,只要1种颜色.

AC代码

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int N=100005;
int primes[N], n, cnt=0;
bool vis[N];

void Lsieve(){// 线性筛
    memset(primes, 0x00, sizeof primes);
    memset(vis, 0x00, sizeof vis);
    for(int i=2; i<=n; ++i){
        if(!vis[i]) primes[cnt++]=i;
        for(int j=0; primes[j]<=n/i; ++j){
            vis[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}

int main(){
    scanf("%d", &n);
    ++n;
    Lsieve();
    if(n<=3) printf("1n");
    else printf("2n");
    for(int i=2; i<=n; ++i){
        if(!vis[i]) printf("1 ");
        else printf("2 ");
    }
    return 0;
}

ACW 196/POJ 2689. 质数距离

题目链接

题目大意

给定两个整数 L L L R R R,其中 1 ≤ L ≤ R ≤ 2 31 1leq L leq R leq 2^{31} 1LR231 R − L ≤ 1 0 6 R-Lleq 10^6 RL106,求区间 [ L , R ] [L, R] [L,R]中相邻两个质数差值最小的数对和差值最大的数对.

解析

如果直接使用线性质数筛筛出所有的质数,那么时间复杂度为 O ( n ) mathcal{O}(n) O(n),考虑到 2 3 1 ≈ 2.1 e 9 2^31approx 2.1e9 2312.1e9所以会TLE.
考虑到 R − L R-L RL是一个比较小的数,因此需要设计一种筛区间质数的算法,考虑可以筛出 x x x的质因子最大值不会超过 x sqrt{x} x ,分两步筛选:

  1. 筛出 2 ∼ R 2sim sqrt{R} 2R 中所有的质数,设质数集合为 P mathbb{P} P
  2. 对于每个 p ∈ P pinmathbb{P} pP,标记 [ L , R ] [L, R] [L,R]中所有能被 p p p整数的数.

算法时间复杂度为 O ( R ln ⁡ ln ⁡ R + ( R − L ) log ⁡ log ⁡ R ) mathcal{O}(sqrt{R}lnlnsqrt{R}+(R-L)loglog R) O(R lnlnR +(RL)loglogR)

AC代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;

const int N=1000005;
typedef long long LL;
int primes[N], n, cnt=0;
bool vis[N];
int l, r;

void Lsieve(int n){ // 线性筛
    memset(primes, 0x00, sizeof primes);
    memset(vis, 0x00, sizeof vis);
    for(int i=2; i<=n; ++i){
        if(!vis[i]) primes[cnt++]=i;
        for(int j=0; primes[j]<=n/i; ++j){
            vis[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}

int main(){
    Lsieve(50000);
    while(cin>>l>>r){
        memset(vis, 0x00, sizeof vis);
        for(int i=0; i<cnt; ++i){
            LL p=primes[i];
            for(LL j=max(2*p, (l+p-1)/p*p); j<=r; j+=p) vis[j-l]=true;
        }
        int knt=0;
        int sub[r-l+1];
        memset(sub, 0x00, sizeof sub);
        for(int i=0; i<=r-l; ++i){
            if(!vis[i] && i+l>1) sub[knt++]=i+l;
        }
        if(knt<2) cout<<"There are no adjacent primes."<<endl;
        else{
            int minp=0, maxp=0; // 存储下标
            for(int i=0; i+1<knt; ++i){
                int d=sub[i+1]-sub[i];
                if(d<sub[minp+1]-sub[minp]) minp=i;
                if(d>sub[maxp+1]-sub[maxp]) maxp=i;
            }
            printf("%d,%d are closest, %d,%d are most distant.n", sub[minp], sub[minp+1], sub[maxp], sub[maxp+1]);
        }
    }
    
    return 0;
}

ACW 197. 阶乘分解

题目链接

题目大意

给定整数 n n n,将 n ! n! n!分解质因数

解析

由算术基本定理

任何大于1的正整数都可以唯一分解为有限个质数的乘积, N = p 1 c 1 p 2 c 2 … p m c m N=p_1^{c_1}p_2^{c_2}dots p_m^{c_m} N=p1c1p2c2pmcm,其中 c i c_i ci是正整数, p i p_i pi是质数且满足 p 1 < p 2 < ⋯ < p m p_1<p_2<dots<p_m p1<p2<<pm.

扫描 2 ∼ N 2sim sqrt{N} 2N 中每个质数 d d d,如果 d ∣ N dmid N dN,则一直执行N/=d运算直到 d ∤ N dnmid N dN,如果最后余下的值不为1,那么也是 N N N的质因子,一并加入结果. 算法时间复杂度为 O ( N ) mathcal{O}(sqrt{N}) O(N ).
算法模板代码如下

void divide(int n){
	int k=0;
	for(int i=2; i<=n/i; ++i){
		if(n%i==0){
			p[k++]=i; c[k]=0; // 记录分解的质因子和其指数
			while(n%i==0) {n/=i; c[k]++;}
		}
	}
	if(n>1){p[++k]=m; c[k]=1;}
}

本题中给出的 N N N达到 1 e 6 1e6 1e6级别,因此按照常规思路逐一分解后累计的方法时间复杂度为 O ( n n ) mathcal{O}(nsqrt{n}) O(nn ),会TLE.
不妨从质因子的角度去考虑该问题,对 2 ∼ n − 1 2sim n-1 2n1所有的质因子进行枚举,一次统计 p , p 2 , … , p k p, p^2,dots, p^k p,p2,,pk贡献的次数,分别为 ⌊ n p ⌋ , ⌊ n p 2 ⌋ , … , ⌊ n p k ⌋ lfloor{frac{n}{p}}rfloor, lfloor{frac{n}{p^2}}rfloor,dots,lfloor{frac{n}{p^k}}rfloor pn,p2n,,pkn个,所以 p p p的指数 I I I表达式为
I = ∑ i = 1 k ⌊ n p i ⌋ I=sum_{i=1}^k lfloor{frac{n}{p^i}}rfloor I=i=1kpin
算法时间复杂度为 O ( n l o g n ∗ l o g n ) = O ( n ) mathcal{O}(frac{n}{log n}*logn)=mathcal{O}(n) O(lognnlogn)=O(n)

AC代码

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
const int N=1000005;
int primes[N], cnt=0;
bool vis[N];

void Lsieve(int n){
    for(int i=2; i<=n; ++i){
        if(!vis[i]) primes[cnt++]=i;
        for(int j=0; primes[j]<=n/i; ++j){
            vis[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}

int main(){
    int n;
    cin>>n;
    Lsieve(n);
    for(int i=0; i<cnt; ++i){
        int p=primes[i];
        int k=0;
        for(int j=n; j; j/=p) k+=j/p;
        cout<<p<<" "<<k<<endl;
    }
    return 0;
}

最后

以上就是眼睛大柜子为你收集整理的【ALGO】数论算法(2) 质数筛例题前文链接例题的全部内容,希望文章能够帮你解决【ALGO】数论算法(2) 质数筛例题前文链接例题所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部