数论质数

质数的筛选:
一、
Eratosthenes筛选 nloglogn
基于思想:
任意整数x的倍数2x,3x都不是质数。
优化:
在筛选中我们发现,2和3都会把6标记为合数,实际上,小于x^2的x倍的数在扫描更小的数时就已经被标记过了

void primes(int n)
{
    bool v[n];        //合数标记
    memset(v,0,sizeof(v));
    for(int i=2;i<=n;i++)
    {
        if(v[i])continue;
        printf("%d ",i);
        for(int j=i;j<=n/i;j++)
            v[j*i]=1;
    }
}
	

二、线性筛选 o (n)
即使在优化后,埃氏筛选仍然会重复标记合数。例如12会被2标记,又会被3标记。根本原因在于我们没有确定出唯一产生12的方式。
步骤:
1、设数组v记录每个数的最小质因子。
2、依次考虑2~N之间每个数。
3、若v[i]=i,说明i是质数,把它保存下来。
4、扫描不大于v[i]的每个质数p,令v[ip]=p。因为p<=v[i],所以p就是合数ip的最小质因子。
每个合数i*p只会被它的最小质因子p筛选一次,时间复杂度为o(n)。

int v[maxn],prime[maxn];
void primes(int n)
{
    memset(v,0,sizeof(v));
    int m=0;
    for(int i=2;i<=n;i++)
    {
        if(v[i]==0)
        {
            v[i]=i;
            prime[++m]=i;  //i是质数
        }
        //给当前的数i乘上一个质因子
        for(int j=1;j<=m;j++)
        {
            //prime[j]有比i更大的质因子,或者超出n的范围,退出。
            if(prime[j]>v[i]||prime[j]>n/i)break;
            //prime[j]是合数i*prime[j]的最小质因子
            v[i*prime[j]]=prime[j];
        }
    }
    for(int i=1;i<=m;i++)
        printf("%d ",prime[i]);
}

    for(int i=2;i<=n;i++)    
    {
        if(!v[i])
        {
            prime[++m]=i;
            phi[m]=i-1;
        }
        for(int j=1;j<=m;j++)
        {
            if(prime[j]*i>n)break;
            v[prime[j]*i]=1;
            if(!(i%prime[j]))
            {
            	phi[i*prime[j]]=phi[prime[j]]*prime[j];
            	break;
            }
            else
            	phi[i*prime[j]]=phi[prime[j]]*(prime[j]-1);
        }
    }

质因子的分解
试除法

void divide(int n)
{
    int m=0;
    for(int i=2;i<=sqrt(n);i++)
    {
        if(n%i==0)
        {
            p[++m]=i,c[m]=0;
            while(n%i==0)n/=i,c[m]++;
        }
    }
    if(n>1)p[++m]=n,c[m]=1;
    for(int i=1;i<=m;i++)
        printf("%d ^ %d",p[i],c[i]);
}

题目

已知正整数a0,a1,b0,b1,a 0 ,a 1 ,b 0 ​,b1 ​ ,设某未知正整数x 满足:

1. x 和 a 0 的最大公约数是 a 1 ​ ;

2. x 和 b 0 ​ 的最小公倍数是b 1 ​ 。

Hankson 的“逆问题”就是求出满足条件的正整数xx。但稍加思索之后,他发现这样的xx
并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 xx 的个数。请你帮助他编程求解这个问题。

解答一https://blog.csdn.net/nuclearsubmarines/article/details/77603154
解答二、

#include <stdio.h>
#include <string.h>
#define min(a,b) a>b?b:a
#define ll long long
ll read()
{
    ll s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
int prime[45000],v[45000];
int m;
void primes()
{
    memset(v,0,sizeof(0));
    for(int i=2;i<=45000;i++)
    {
        if(v[i]==0)
        {
            v[i]=i;
            prime[++m]=i;
        }
        for(int j=1;j<=m;j++)
        {
            if(prime[j]>v[i]||prime[j]>45000/i)break;
            v[i*prime[j]]=prime[j];
        }
    }
}
int get_num(ll a,ll b)
{
    int num=0;
    while(a%b==0)
    {
        a/=b;
        num++;
    }
    return num;
}
int main()
{
    primes();
    int t=read();
    while(t--)
    {
        ll a=read(),c=read(),b=read(),d=read();
        if(d==1)
        {
            if(c==1)printf("1\n");
            else printf("0\n");
            continue;
        }
        int ma,mb,mc,md,mx;
        ll ans=1;
        for(int i=1;i<=m&&prime[i]<=d;i++)
        {
            if(d%prime[i]==0)
            {
            	mx=0;
                md=get_num(d,prime[i]);
                ma=get_num(a,prime[i]);
                mb=get_num(b,prime[i]);
                mc=get_num(c,prime[i]);
                while(d%prime[i]==0)d/=prime[i];
                if(ma>mc&&mb<md&&md==mc)mx++;
                else if(ma>mc&&mb==md&&mc<=md)mx++;
                else if(ma==mc&&mb<md&&mc<=md)mx++;
                else if(ma==mc&&mb==md&&mc<=md)mx=md-mc+1;
                ans*=mx;
            }
        }
        if(d>1)
        {
        	mx=0;
            md=get_num(d,d);
            ma=get_num(a,d);
            mb=get_num(b,d);
            mc=get_num(c,d);
            if(ma>mc&&mb<md&&md==mc)mx++;
            else if(ma>mc&&mb==md&&mc<=md)mx++;
            else if(ma==mc&&mb<md&&mc<=md)mx++;
            else if(ma==mc&&mb==md&&mc<=md)mx=md-mc+1;
            ans*=mx;
        }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值