【数论】质因数分解+唯一分解定理

任何一个大于1的正整数都能唯一分解为有限个质数的乘积。

1. 试除法

可以扫描2-sqrt(n)的每个数d,若d能整除N,则从N中除掉所有的因子d,同时累计除去d的个数。

//质因数分解试除法
void divide(int n)
{
    m=0;
    for(int i=2;i<=sqrt(n);i++)
    {
        if(n%i==0)
        {
            p[++m]=i;c[m]=0;
            while(n%i==0)
            {
                c[m]++;
                n/=i;
            }
        }
    }
    if(n>1)
        p[++m]=n,c[m]=1;
}

例题 POJ 2689 Prime Distance

题意:
给定两个整数L,R(1<=L<=R<=231,R-L<=106)。求闭区间[L,R]中相邻两个质数的差最大时多少,输出这两个质数。最小是多少,输出这两个质数。

思路:
可以看到虽然L,R的范围很大,但是R-L的范围却很小。所以不妨先将2- R \sqrt R R 的质数筛出来。对于任意一对L-R,扫描素数p,标记i*p(L/p <= i<= R/p)
扫描结束后,没有被标记到的就是剩下的质数。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=1e9;
const int maxn=5e4+10;
const int maxm=1e6+10;
int l,u,prime[maxn],vis[maxn],f[maxm],t;
void init()//求出50000内的素数,int范围内的合数最小质因子必定小于2^16。
{
    int i,j,k,m;
    t=0;
    m=(int)sqrt(maxn+0.5);
    memset(vis,0,sizeof(vis));
    for(i=2;i<=m;i++)
    {
        if(!vis[i])
        {
            for(j=i*i;j<maxn;j+=i)
            vis[j]=1;
        }
    }
    for(i=2;i<maxn;i++)if(!vis[i])prime[t++]=i;
}
int main()
{
    init();
    while(cin>>l>>u)
    {
        if(l==1)l=2;//注意l=1会出问题。
        int i,j,k,a,b;
        memset(f,0,sizeof(f));
        for(i=0;i<t;i++)
        {
            a=(l-1)/prime[i]+1;
            b=u/prime[i];
            for(j=a;j<=b;j++)if(j>1)f[j*prime[i]-l]=1;//[l,u]区间小于1e6,而l,u数值范围为int,所以偏移l们就能用数组存了
        }
        int p=-1,max_ans=-1,min_ans=INF,x1,y1,x2,y2;
        for(i=0;i<=u-l;i++)//暴力枚举。。
        {
            if(f[i]==0)
            {
                if(p==-1){p=i;continue;}
                if(max_ans<i-p){max_ans=i-p;x1=p+l;y1=i+l;}
                if(min_ans>i-p){min_ans=i-p;x2=p+l;y2=i+l;}
                p=i;
            }
        }
        if(max_ans==-1)cout<<"There are no adjacent primes."<<endl;
        else cout<<x2<<","<<y2<<" are closest, "<<x1<<","<<y1<<" are most distant."<<endl;
    }
    return 0;
}

例题 CH3101 阶乘分解

题意:
给定整数N(1<=N<=10^6),试把阶乘N!分解质因数,按照算数基本定理的形式输出分解结果中的pi和ci即可。

思路:
可以先筛出1-N的素数,然后考虑N!阶乘中有多少个素数p。
N!中质因子p的个数等于1-N每个数包含质因子p的个数之和。
在1-N中,p的倍数,至少包含一个质因子p;
在1-N中,p^2的倍数,至少包含2个质因子p,但是在p的时候已经加过一个了,所以这里只需加1;

一直到 p^k<=N为止。limit等于 l o g p N log_p^N logpN

时间复杂度比O(Nlog(N))稍小。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 

const int MAXN=1e6+10;
int prime[MAXN+1];
void getprime()
{
    memset(prime,0,sizeof(prime));
    for(int i=2;i<=MAXN;i++)
    {
        if(!prime[i])
        {
            prime[++prime[0]]=i;
        }
        for(int j=1;j<=prime[0]&&prime[j]<=MAXN/i;j++)
        {
            prime[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}

int sum[MAXN+10];
int main()
{
    int n;
    getprime();
    cin>>n;
    for(int i=1;prime[i]<=n;i++)
    {
        int limit=log10(n)/log10(prime[i]);
        for(int j=1;j<=limit;j++)
        {
            sum[prime[i]]+=(n/pow(prime[i],j));
        }
    }
    for(int i=2;i<=MAXN;i++)
    {
        if(sum[i]!=0)
            cout<<i<<"  "<<sum[i]<<endl;
    }

    return 0;
}

2. 唯一分解定理

LightOJ - 1236

题意:
求1=<i<=n,1<=j<=n,lcm(i,j)=n的方案数。

思路:
n = p1 ^ e1 * p2 ^ e2 *…*pn ^ en

for i in range(1,n):

    ei 从0取到ei的所有组合

必能包含所有n的因子。

现在取n的两个因子a,b

a=p1 ^ a1 * p2 ^ a2 *…*pn ^ an

b=p1 ^ b1 * p2 ^ b2 *…*pn ^ bn

gcd(a,b)=p1 ^ min(a1,b1) * p2 ^ min(a2,b2) *…*pn ^ min(an,bn)

lcm(a,b)=p1 ^ max(a1,b1) * p2 ^ max(a2,b2) *…*pn ^ max(an,bn)

先对n素因子分解,n = p1 ^ e1 * p2 ^ e2 *…*pk ^ ek,

lcm(a,b)=p1 ^ max(a1,b1) * p2 ^ max(a2,b2) *…*pk ^ max(ak,bk)

所以,当lcm(a,b)==n时,max(a1,b1)==e1,max(a2,b2)==e2,…max(ak,bk)==ek

当ai == ei时,bi可取 [0, ei] 中的所有数 有 ei+1 种情况,bi==ei时同理。

那么就有2(ei+1)种取法,但是当ai = bi = ei 时有重复,所以取法数为2(ei+1)-1=2ei+1。
除了 (n, n) 所有的情况都出现了两次 那么满足a<=b的有 (2
ei + 1)) / 2 + 1 个

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=1e7+5;
const int NN=1e6;
unsigned int prime[NN],cnt;           //prime[N]会MLE
bool vis[N];

void is_prime()
{
    cnt=0;
    memset(vis,0,sizeof(vis));
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
        {
            prime[cnt++]=i;
            for(int j=i+i;j<N;j+=i)
            {
                vis[j]=1;
            }
        }
    }
}

int main()
{
    is_prime();
    int t;
    cin>>t;
    for(int kase=1;kase<=t;kase++)
    {
        LL n;
        cin>>n;
        int ans=1;
        for(int i=0;i<cnt&&prime[i]*prime[i]<=n;i++)
        {
            if(n%prime[i]==0)
            {
                int e=0;
                while(n%prime[i]==0)
                {
                    n/=prime[i];
                    e++;
                }
                ans*=(2*e+1);
            }
        }
        if(n>1)
            ans*=(2*1+1);
        printf("Case %d: %d\n",kase,(ans+1)/2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值