数论:整数的唯一分解定理及其应用小结

最近听了一首歌(egoist的《永远》,恕我不会写日文),现在满脑子都是这首歌非常的。。。不愉快,没有办法集中啊!!!QAQ太TM好听了ORZ

为了把这首歌洗掉,来做一个总结吧,内容如上所述。

整数的唯一分解定理:一个大于1的整数一定可以被分解成若干质数的乘积,即X=e1^k1 * e2^k2 * …… * en^kn=mul{ei*ki | 1<= i <= n},X >= 2,e是质数。下面的表达都基于这里的定义。

证明:很容易。一个大于1的整数要么是质数,要么是合数。质数分解之后就是它自己,合数可以被除了1和自身以外的数整除(可以整除合数的数可以是质数也可以是合数,显然一个整数不能够被无穷无尽的整数整除),这样递归地思考就可以发现一定可以被分解成若干个质数的乘积形式。

下面先给出我的分解代码。代码中采用结构体的形式保存了分解之后指数不为0的项。这是根号n的试除法,正确性容易证明就略去吧(不想当婆婆嘴)。


struct term{ int e,k; };
void getzys(int n,vector<int>&a)
{
    if(n<2) return;//要不要都无所谓,习惯而已
    a.clear();
    int m=(int)sqrt(n+0.5);
    term tmp;
    for(int i=2;i<=m;i++) if(n%i==0)
    {
        tmp=(term){i,0};
        while(n%i==0) tmp.k++,n/=i;
        a.push_back(tmp);
    }
    if(n>1) a.push_back((term){n,1});//注意这一句
}

下面开始讲一些基础应用。
n!质因数分解:给出正整数n,请编程输出n!的标准分解式(形如“4!=2^3*3^1”)。n <= 10^6。
分析:由于n!的特殊性,可以发现小于等于n的质数一定都是n!的质因数,大于n的质数一定不是。那么先打出质数表(推荐杜教筛,新操作!)。对于质数ei,在n!的分解式中的指数ki=n/ei+n/ei^2+……+n/ei^x,其中n>=ei^x。 还是很好证明的,想象一下一个阶梯形状·····
实现代码(杜教筛和分解):

void make_prime_table(int n)
{
    memset(isp,1,sizeof(isp));
    isp[0]=isp[1]=0;
    for(int i=2;i<=n;i++)
    {
        if(isp[i]) pr.push_back(i);
        for(int j=0;j0;
            if(i%pr[j]==0) break;
        }
    }
}
void getzys_j(int n,vector&a)
{
    a.clear();
    for(int i=0;iif(pr[i]>n) break;
        term tmp;
        tmp=(term){pr[i],0};
        LL t=pr[i];
        while(t<=n) tmp.k+=n/t,t*=pr[i];
        a.push_back(tmp);
    }
}

n的因数: 给定整数n(2 < n < 2*10^9),请计算n的因数的个数以及所有因数的和。
分析:知道了唯一分解定理之后第一个问题就很简答了呀,直接上乘法原理 ans1 = (k1+1)(k2+1)……(kn+1) 。1就代表在该因数中对应的那一项没有的情况。第二问上乘法分配律 ans2= (1+e1^1+e1^2+……+e1^k1)(1+e2^1+e2^2+……+e2^k2)……(1+en^1+en^2+……+en^kn)

整除性: 给定整数n,m,s(可能有多个),试判断n^m是都可以被s整除。0 < n <= 10^9 0 <= m <= 300000 0 < s <= 10^18 最多可能有100000个s。
分析:直接把n分解后的指数乘以m就没毛病了。用所有的质因子除s看最后s是不是等于1。(虽然分解s看起来很正解但是。。TL到死)

假的反质数: 给定整数n,请在[1..2*10^9]范围内寻找有n个因数的最小的整数!
分析:有一个东西叫做反质数(这并不是这个问题要求解的数),反质数要求所有小于它的非0自然数的因数个数都小于它。

。。嗯一开始做这个题的时候有一个很粗暴的想法就是答案一定是2的多少次方,想法的来源是一个数字的质因子个数确定的时候因子的数量是一定的。这个想法有个关键的错误,单看质因子个数和因数个数之间实际上并没有任何的定量关系。比如2^2和2^1*3^1,因为相同的质因子相搭配的时候实际上是没有对因数的个数作出任何新的贡献的。
但是有一个最关键的地方没有变——肯定要让所有的质因子尽量小,即在已知所有的指数的情况下让所有的底数尽量小,这显然是正确的,那么所有的底数应该是连续的。然后注意到一点,在指数一定的情况下,一定是让最小的底数搭配最大的指数,这是个显然的贪心策略。
于是采用回溯算法。
先打出一张质数表,在这张表的基础上进行回溯。加上人为定序(非递增)和最优性剪枝,可以想象一下人为定序之后以方案数作为剪枝条件实际上跑起来应该是相当快的(用因数个数估计一下)。

下面是实现的代码:

#include
#include
#include
#include
#include
#include
#define inf 2e9+5
using namespace std;
typedef long long LL;

int N,ans;
int p[]={0,2,3,5,7,11,13,17,19,23,29,0};

void run(int i,int last,int cnt,int num)
{
    if(cnt>N) return;
    if(cnt==N) { ans=num; return; }
    for(int j=1;j<=last;j++)
    {
        if((LL)num*p[i]>2e9||(LL)num*p[i]>=ans) break;
        num*=p[i];
        run(i+1,j,cnt*(j+1),num);
    }
}
int main()
{
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    while(scanf("%d\n",&N)==1)
    {
        ans=inf;
        run(1,30,1,1);
        if(ans==inf) ans=-1;
        printf("%d\n",ans);
    }
    return 0;
}

欧拉函数phi:求小于等于n的和n互质的数的数量。
公式:phi(n) = n(1-1/e1)(1-1/e2)……(1-1/en),容斥很爆炸。


大概就是酱了吧,还有很多灵活的应用。比如gcd和lcm(最小公倍数和最大公因数)的质因数分解表示。只是有的时候脑子一卡就。。。很气QAQ

你可能感兴趣的:(整数唯一分解定理,数论,总结)