唯一分解定理又稱為算數基本定理,基本內容是:
每個大於1的自然數,要么本身就是質數,要么可以寫為2個或以上的質數的積,而且這些質因子按大小排列之后,寫法僅有一種方式。
用另一種方法表示就是:
對於任何一個大於1的正整數,都存在一個標准的分解式: N=p1^a1 * p2^a2*···*pn^an;(其中一系列an為指數,pn為質數)
此定理表明:任何一個大於 1 的正整數都可以表示為素數的積。
有這樣幾個式子:
設F(n)代表n的正因子的數量,則F(n)=(a1+1)*(a2+1)*(a3+1)*······*(an+1);
設G(n)代表n的正因子的和,則G(n)=(1+p1^2+p1^3+...+p1^a1)*(1+p2^2+p2^3+...p2^a2)*....*(1+pn^1+pn^2+...+pn^an)=;
獲得一個數正因子數量的代碼:
primel [ ]是素數表
1 ll getfac(ll x) 2 { 3 ll ans=1; 4 for(int i=1;i<=cnt&&primel[i]<=x;i++) 5 { 6 ll sum=0;//當前質因數的冪指數 7 while(x%primel[i]==0)//當是這個數的因子時 8 { 9 sum++; 10 x/=primel[i]; 11 } 12 ans*=(sum+1);//應用定理的結論 13 } 14 if(x>1)//當搜索不到的時候,如果這個數最后大於一,那么這個最后結果肯定是素數,並且指數是1 15 ans*=2; 16 return ans; 17 }
來看一個應用此定理的例題:
鏈接:Light OJ 1341 Aladdin and the Flying Carpet
此題的題意就是,給定一個矩形(非正方形)的面積 a,求可以分解的長*寬有多少組,且任意一條邊不能小於b,不能重復(4*3和3*4重復)。
即求一個數可以分解為多少個正因數相乘,想到唯一分解定理,用它把所有可能的因子找出后,再除以2,在把小於b的因子暴力去除。
看代碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 typedef long long ll; 6 const int maxn=1e6+20; 7 int primel[maxn]; 8 bool isp[maxn]; 9 int cnt; 10 void makel()//歐拉篩打印素數表 11 { 12 cnt=0; 13 memset(isp,true,sizeof(isp)); 14 for(int i=2;i<maxn;i++) 15 { 16 if(isp[i]) 17 primel[++cnt]=i; 18 for(int j=1;j<=cnt;j++) 19 { 20 if(i*primel[j]>maxn) 21 break; 22 isp[i*primel[j]]=false; 23 if(i%primel[j]==0) 24 break; 25 } 26 } 27 } 28 ll getfac(ll x)//唯一分解定理 29 { 30 ll ans=1; 31 for(int i=1;i<=cnt&&primel[i]*primel[i]<=x;i++) 32 { 33 ll sum=0; 34 while(x%primel[i]==0) 35 { 36 sum++; 37 x/=primel[i]; 38 } 39 ans*=(sum+1); 40 } 41 if(x>1) 42 ans*=2; 43 return ans; 44 } 45 int main() 46 { 47 int t; 48 int cas=0; 49 ll a,b; 50 cin>>t; 51 makel(); 52 while(t--) 53 { 54 scanf("%lld%lld",&a,&b); 55 if(a<b*b)//不可能分解為比b大的數相乘 56 { 57 printf("Case %d: 0\n",++cas); 58 continue; 59 } 60 ll count=getfac(a)/2;//不能重復 61 for(int i=1;i<b;i++)//去除小於b的因子 62 { 63 if(a%i==0) 64 count--; 65 } 66 printf("Case %d: %lld\n",++cas,count); 67 } 68 return 0; 69 }
再看一個題:
這個題運用了分解質因子和的公式(原理)。
鏈接:LightOJ 1136 Sigma Function
這個題其實就是求1~n所有的數中,可分解為質因子的和為偶數的數有多少個。
這個其實就是那個和的公式(結論)。
把上面的結論搬下來,那個公式G(n)=(1+p1^2+p1^3+...+p1^a1)*(1+p2^2+p2^3+...p2^a2)*....*(1+pn^1+pn^2+...+pn^an);
就是要讓它等於偶數,分析一下,這首先是乘積式,如果要讓乘積為偶數,那么每一項就是偶數,可是這樣的話pi如果偶數那么所有pi為偶數的G(n)都是奇數(pi^ai一定為偶,再加個1),在剩下的情況中,得分很多情況,定TLE。於是想反面,求多少個G(n)為奇數,再減去。
如果要讓和為奇數,想奇*偶=偶,奇*奇=奇,偶*偶=偶,於是只要讓每一項(1+pi^ai)為奇數即可,如果p[i] = 2;,則其和一定為奇數(2的冪次和為偶+1),除了2以外,素數都是奇數,所以 (p[i]^0 + p[i]^1 +.....+ p[i]^e[i])要滿足是奇數的話e[i]一定是偶數(首先奇數任何次冪仍為奇數,奇+奇=偶兩兩組合后一定還剩一個奇數,然后偶+奇=奇),如果 n 是一個平方數,那么他的約數和一定是奇數,如果2*n是平方數,他的約數和也是奇數。
代碼如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 typedef long long ll; 6 int main() 7 { 8 int t,cas=0; 9 ll n; 10 cin>>t; 11 while(t--) 12 { 13 scanf("%lld",&n); 14 ll ans; 15 ans=n-(ll)sqrt(n)-(ll)sqrt(n/2); 16 printf("Case %d: %lld\n",++cas,ans); 17 } 18 return 0; 19 }