(数论)最大公约数、最小公倍数、唯一分解定理

一、最大公约数gcd

约数和倍数的定义(百度百科)

整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。
显然,任何非0整数是0的约数,0不是任何数的约数。

int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}

二、最小公倍数lcm

定理:lcm(a,b)gcd(a,b)=ab

证明:设x和y的最大公约数为a,则最小公倍数为(x/a)(y/a)a=xy/a,最大公约数和最小公倍数的乘积为xy/aa=xy,证毕。

int lcm(int a,int b){
    return a/gcd(a,b)*b;//注意技巧:先除再乘,可避免乘法溢出
}

三、唯一分解定理

任何一个大于1的自然数N,都可以唯一分解成有限个质数的乘积N=p1a1*p2a2*…*pn^an这里p1<p2<…<pn均为质数,各指数ai是正整数。
这样的分解称为N的标准分解式。

定理应用:
一个大于1的自然是N,根据其标准分解式,可以求出其正因子个数是(a1+1)(a2+1)…(an+1),全体正因子之和是(1+p1+p12+…+p1a1)(1+p2+p22+…+p2a2)…(1+pn+pn2+…+pnan)。

下面是唯一分解定理的粗糙代码实现:

#include<iostream>
#include<cmath>
using namespace std;

typedef struct{
    int x,y;
}node;

node a[1000];
int j=0;

int solve(int n){
    j=0;
    for(int i=2;i<=n;i++){
        if(n%i==0){
            int cnt=1;
            n/=i;
            while(n%i==0){
                cnt++;
                n/=i;
            }
            a[j].x=i;
            a[j++].y=cnt;
        }
    }
}

int main()
{
    int T,n;
    cin>>T;
    while(T--){
        cin>>n;
        solve(n);
        for(int i=0;i<j;i++){
            cout<<a[i].x<<" "<<a[i].y<<endl;
        }
    }
    return 0;
}

【例题Choose and divide UVA - 10375 】

题意:已知C(m,n)=m! / (n!*(m-n!)),输入整数p,q,r,s(p>=q,r>=s,p,q,r,s<=10000),计算C(p,q)/C(r,s)。输出保证不超过10^8,保留5位小数

【代码】
参考刘汝佳《算法竞赛入门经典》(第2版)

#include<iostream>
#include<cstring>
#include<vector>
#include<cmath>
#include<cstdio>
using namespace std;

const int maxn=10000+5;

vector<int>prime;
bool vis[maxn];
int e[maxn];

//把10000以内的所有素数给晒出来
void init(){
    memset(vis,0,sizeof(vis));
    for(int i=2;i<maxn;i++)
        for(int j=2*i;j<maxn;j+=i)
            vis[j]=1;
    for(int i=2;i<maxn;i++)
        if(!vis[i]) prime.push_back(i);
}
//一个一个整数用素数给消耗掉,转化成素数指数幂形式(唯一分解定理)
void add_integer(int n,int d){
    for(int i=0;i<prime.size();i++){
        while(n%prime[i]==0){
            n/=prime[i];
            e[i]+=d;
        }
        if(n==1) break;
    }
}
//把整个阶乘按照一个数一个数的拆成指数幂
void add_factorial(int n,int d){
    for(int i=1;i<=n;i++)
        add_integer(i,d);
}

int main()
{
    init();
    int p,q,r,s;
    while(cin>>p>>q>>r>>s){
        memset(e,0,sizeof(e));
        add_factorial(p,1);
        add_factorial(q,-1);
        add_factorial(p-q,-1);
        add_factorial(s,1);
        add_factorial(r-s,1);
        add_factorial(r,-1);
        double ans=1;
        for(int i=0;i<prime.size();i++)
            ans*=pow(prime[i],e[i]);
        printf("%.5lf\n",ans);
    }
    return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值