數論:整數的唯一分解定理及其應用小結

最近聽了一首歌(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;j<pr.size()&&(LL)i*pr[j]<=n;j++)
        {
            isp[i*pr[j]]=0;
            if(i%pr[j]==0) break;
        }
    }
}
void getzys_j(int n,vector<term>&a)
{
    a.clear();
    for(int i=0;i<pr.size();i++)
    {
        if(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<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章