18省夏day1

1、素因数分解

       1、https://www.luogu.org/problemnew/show/P1075

       2、https://www.luogu.org/problemnew/show/CF776B:这道题显然可以将素数化为                         1,合数变为2,然后因为长度到了100000,所以应使用埃氏筛法。但实践是一直有问题,1、要注意i等于1和2的特例;2、

       3、整数惟一分解定理:任意一个大于0的正整数都能被表示成若干个素数的乘积且表示方法是唯一的;整理可以将相同素数的合并;可以得到公式————n = P1^a1 * P2^a2 * …………*Pn^an (P1 < P2 < ……Pn);有一些小细节要注意

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n;
int a[10000];
int p[10000]; 
int main(){
    scanf("%d",&n);
    int m=n;//注意应用m代替n,不用中途会变小 
    int count=0;
    for(int i=2;i*i<=n;i++){
        while(m%i==0){//因为一定会把带有这个素数的除完,所以肯定不会被合数除 
            p[i]++;    //这样写好写方便 
            m/=i;
        }
    }
    if(m>1)p[m]++;//重点!!最后可能会多一个出来,要把本身也算 
    for(int i=2;i<=n;i++)
        if(p[i])
            printf("%d^%d
",i,p[i]);
}
View Code

 2、素数筛法

       1、埃氏筛法 埃氏筛法的中心还是很好理解,只是要注意从j=i*i开始,降低时间消耗

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,a[1001001];
int main(){
    scanf("%d",&n);
    a[0]=a[1]=1;//将0和1特判,素数 为0
    for(int i=2;i*i<=n;i++){
        if(!a[i]){
            for(int j=i*i;j<=n;j+=i)
                a[i]=1;
        }
    } 
    for(int i=2;i<=n;i++){
        printf("%d ",a[i]);
    }
    return 0;
} 
View Code 

       2、Prime Distance:https://www.luogu.org/problemnew/show/UVA10140   这道题还是应用埃氏筛法,但是这道题显然数据太大,不能筛完全部素数,所以我们可以考虑先筛出2到根号r的所有素数(为了节省时间,要先做,直接到100000>根号的2^31-1),然后注意到l和r间的的范围是有限的,只有1000000,所以就用已算出的素数将其筛掉,即标记i*p-l 为合数(减去l是精华!,然后再用prime2将l到r的素数记录下来即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int prime1[100100];
long long prime2[100100];//注意大小,要用ll 
bool check[1000100];//用bool占用的空间较小 
int count1=1,count2;
int main(){//往往错在最没想到会错的地方 
    for(int i=2;i*i<=100100;i++)
        if(!check[i]){//写错 
            prime1[count1++]=i;
            for(long long j=i*i;j<=100100;j+=i)//j应+=i 
                check[j]=1;//写错 
        }    
/*    for(int i=1;i<=10;i++)
        cout<<prime1[i]<<"   ";*/
    long long L,R;    
    long long mina,maxa,x1,x2,y1,y2;
    while(scanf("%lld%lld",&L,&R)!=EOF){
        mina=131313133,maxa=-1;
        x1=x2=y1=y2=0;
        memset(check,0,sizeof(check));
        count2=0;
        L=max(L,(long long)2);//要注意1是特例 
        for(long long i=1;prime1[i]*prime1[i]<=R&&i<count1;i++){//注意在prime1中素数只有count1个 
            long long s=L/prime1[i]+(L%prime1[i]>0);//括号位置!因为范围在L,R间l/prime向下取整,除非正好相等否则要加1 
            if(s==1)s=2;//因为prime本身是素数,如果*1也还是素数,如果从1开始就会把素数判成合数 
            for(long long j=s;j*prime1[i]<=R;j++)//j*prime[i]只有小于r才有意义 
                check[j*prime1[i]-L]=1;
        }
    /*    for(int i=0;i<335;i++)
            printf("%d  ",check[i]);*/
        for(long long i=0;i<=R-L;i++)//重点,i的取值范围,l可能正好是素数,最多到R-l 
            if(!check[i])prime2[count2++]=i+L;//count2记区间有几个素数 
    /*    for(int i=0;i<count2;i++)
            printf("%d  ",prime2[i]);*/
        for(int i=1;i<count2;i++){
            if(prime2[i]-prime2[i-1]<mina){
                mina=prime2[i]-prime2[i-1];
                x1=prime2[i-1],y1=prime2[i];
            }
            if(prime2[i]-prime2[i-1]>maxa){
                maxa=prime2[i]-prime2[i-1];
                x2=prime2[i-1],y2=prime2[i];
            }
        }
        if(count2<2)
            printf("There are no adjacent primes.
");
        else{
            printf("%lld,%lld are closest, %lld,%lld are most distant.
",x1,y1,x2,y2);
        }
    }
    return 0;                
}
View Code

      3.利用埃氏筛法优化质因数分解,只要找到在判素数时同时找到最小素因数即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
int prime[1001000],minf[1001000];//确定最小质因数 
vector<int > ret;
//做确定素数时要考虑取值范围,是否要用long long来定义 
void erato(int n){
    prime[0]=prime[1]=0;
    minf[0]=0;minf[1]=1;//特例 
    for(int i=2;i<=n;i++)
        prime[i]=1,minf[i]=i;//当他本身是最小的要么是素数,要么还没找到 
    for(int i=2;i*i<=n;i++)
        if(prime[i]){
            for(int j=i*i;j<=n;j+=i){
                prime[j]=0;
                if(minf[j]=j)
                    minf[j]=i;
            }
        }
}
int found(int x){
    while(x>1){
        ret.push_back(minf[x]);
        x/=minf[x];
    }
}
int main(){
    int n;
    scanf("%d",&n);
    erato(n);
    found(n);
    return 0; 
}
View Code

      4.阶乘分解:将阶乘n!(1<=n<=10^6)分解素因数,以唯一分解形式给出pi和ri                朴素想法将1~n质因数分解,再将相同的pi累加,但是这样显然过不了

然后我们考虑,其实在1~n中,素数也就那几个,所以先找出全部的素数,然后对于每个素数,,N/p^k向下取整就是p出现的个数(通过举例可验证或,因为在n内,对于p的倍数有n/p个,如果有的是p^2的倍数,那么也出现n/p^2个,同理可证)

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,prime[10010],cs[10010],f[1010010];
int cnt=1;
void foun(){
    for(int i=2;i<=n;i++){
        if(!f[i]){
            prime[cnt]=i;cnt++;
            for(int j=i*i;j<=n;j+=i)
                f[j]=1;
        }
    }
    cnt--;
}
void jl(){
    for(int i=1;i<=cnt;i++){
        for(int j=prime[i];j<=n;j*=prime[i])
            cs[i]+=n/j;
    }
}
int main(){
    scanf("%d",&n);
    foun();//先找出一到n中所有存在的质数 
    jl();//找每个素数被用的次数 
    for(int i=1;i<cnt;i++)
        printf("%d^%d+",prime[i],cs[i]);
    printf("%d^%d",prime[cnt],cs[cnt]);
}
View Code

      5.欧拉筛法(线性筛):利用 i 和素数表中的素数 prime[j] 去筛除 i*prime[j] ,为了确保i*prime[j] 只被素数 prime[j] 筛除过这一次,要确保 prime[j] 是i*prime[j] 中最小的素因子,即 i 中不能有比 prime[j] 还要小的素因子

//线性筛法
void Euler(){
    for(int i=2;i<=n;i++){
        if(!isprime[i])prime[++prime[0]]=i;
        else{
            for(int j=1;j<=prime[0]&&prime[j]*i<=n;j++){//每次用已确定的素数来筛 
                isprime[prime[j]*i]=1;
                if(i%prime[j]==0)break;//这句话是线性筛精华所在,保证每个数只被
                //它最小的素因子筛去; 
            }
        }
    }
} 
View Code
原文地址:https://www.cnblogs.com/linzeli/p/9291984.html