約數詳解

約數這個還是數論比較熱門的考點(這邊是提高組,省選大佬出門右轉)。
首先約數是什麼?
聽起來好高大上,其實就是因子,而對於約數要掌握的就是約數和以及約數個數,我們先看一下籠統的計算公式。
對於一個數我們可以質因數分解,所以對於每個數n,我們可以分解成p1 ^ c1+p2 ^ c2+……+px ^ cx,這種形式,而約數個數也就是(c1+1) * (c2+1) * …… (cx+1),而約數和就是(1+p1+p1^ 2 +p1^ 3+p1^ 4……+p1^ c1) (1+1+p2+p2^ 2+p2^ 3+p2^ 4…… +p2^ c2)……(1+pn+pn^ 2+pn^ 3……pn^ cn),因爲這個證明比較麻煩,而且我們這個也不常用,主要是在推導公式時要知道。而根據這個公式我們可以得出一個約數和以及約數個數的算法,先把n質因數分解,再帶公式,可是這個太麻煩了,瑣碎,步驟很多,所以我們在此介紹一種線性篩選的方法,可以在差不多的時間和空間內方便的計算出來,而且一計算就是一串,很方便。
約數個數:
對於線性篩選,我們其實就是在篩選質數的時候順便求出約數個數,我們知道線性篩選就是篩出質數然後再枚舉最小質因子去進行篩選。我們先定義兩個數組t,和e,t[i]表示i這個數約數個數,e[i]表示i這個質因數分解後最小質因子的次方,即c1。爲什麼要這個e數組呢?後面你就知道了。
我們對x進行分類討論,如果x是質數的話,那麼t[x]=2,e[x]=1。這個就是質數的因子只有自己和1嘛,e[x]=1是因爲1不是質數,所以i的最小質因子是自己,這個當然就是1了。
然後我們就到了篩質數的環節了,在這裏我們會處理x不是質數的情況,對於x我們可以在篩時這麼寫i*prime[j](這個是篩質數的操作,prime數組是存質數的),所以我們還要對i和prime[j]進行分類討論。
對於i是prime[j]的倍數,那麼t[x]=t[i]/(e[i]+1) * (e[i]+2),爲什麼是這個?我們先看e數組把,因爲和e數組有關,因爲i是prime[j](我們是枚舉最小質因子去篩的)的倍數所以x的最小質因子指數會多1,所以e[x]=e[i]+1,所以我們根據之前給的約數個數公式可以知道其實就是除以(e[i]+1),乘以(e[i]+2),這就是上面公式的由來。
對於i不是prime[j]的倍數,那麼e[x]=1,因爲prime[j]是我們枚舉的最小質因子,所以這個只有一個。因爲約數個數這個函數是積性函數,所以t[x]=t[i] * t[prime[j]],因爲t[prime[j]]的值一定是2,因爲prime[j]是質數,所以我們可以簡化寫成t[x]=t[i] * 2。
這樣我們就討論完了。
代碼:

#include<iostream>
#include<cstdio>
using namespace std;
long long prime[3000100],t[3000100],e[3000100];
int vist[3000100];
int main()
{
	int n,num=0;
	scanf("%d",&n);
	t[1]=1;//初值,1的約數個數是1 
	for(int i=2;i<=n;i++)
	{
		if(vist[i]==0)//質數的話 
		{
			prime[++num]=i;
			t[i]=2;
			e[i]=1;
		}
		for(int j=1;j<=num;j++)//用最小質因子去篩 
		{
			if(i*prime[j]>n)//超過了n就不用了 
				break;
			vist[i*prime[j]]=1;//打下標記是合數 
			if(i%prime[j]==0)//分類討論 
			{
				t[i*prime[j]]=t[i]/(e[i]+1)*(e[i]+2);//根據我們的公式計算 
				e[i*prime[j]]=e[i]+1;
				break;
			}
			else //不是倍數的時候 
			{
				t[i*prime[j]]=t[i]*2;
				e[i*prime[j]]=1;
			}
		}
	}
	for(int i=1;i<=n;i++)
		printf("%lld\n",t[i]);//輸出答案 
	return 0;
}

約數和:
既然有約數個數,那麼跟定有約數和,現在我們就來講一下怎麼計算約數和。
根據上面約數個數的思想,我們也用分類討論來計算約數和。
同樣我們要定義兩個數組,一個是t,t[i]表示i的約數和,同樣我們也要一個輔助的數組e,e[i]表示不能被最小質因子整除的約數的和。
首先如果x是質數的話,約數只有自己和1,那麼t[x]=x+1,而不能被最小質因子整除的數也只有1,所以e[x]=1。
然後我們再用i*prime[j]去表示非質數的x,同樣,也分i可以整除prime[j]和不能整除。
不可以整除的話,意味着t[i]和t[prime[j]]這兩個是沒有重複部分的,因爲他們這兩個數是互質的,證明一下吧,因爲prime[j]是個質數,所以如果不是互質的話只有一種可能,就是i是prime[j]的倍數(這就是我們要這麼分類討論的原因,上面的分類討論也是因爲這個)。所以t[x]=t[i]*prime[j]+t[i],化簡一下就是t[x]=t[i] * (prime[j]+1)。而e因爲不是倍數所以e[x]=t[i]
如果i是prime[j]的倍數的話因爲他們中有重複的,重複的部分就是可以被最小質因子整除的約數和,也就是t[i]-e[i],那麼我們爲什麼不用e數組直接存這個呢?因爲不方便計算啊。所以我們就是t[x]=t[i] * prime[j]+t[i]-(t[i]-e[i]),化簡一下就是t[x]=t[i]*prime[j]+e[i],而e因爲是倍數所以我們要繼承一下e[x]=e[i],這樣就計算完了。
代碼:

#include<iostream>
#include<cstdio>
using namespace std;
long long vist[3000100],prime[3000100],t[3000100],e[3000100];
int main()
{
	int n,num=0;
	scanf("%d",&n);
	t[1]=1;//邊界 
	for(int i=2;i<=n;i++)
	{
		if(vist[i]==0)//質數的情況 
		{
			prime[++num]=i;
			t[i]=i+1;
			e[i]=1;
		}
		for(int j=1;j<=num;j++)
		{
			if(i*prime[j]>n)
				break;
			vist[i*prime[j]]=1;//打個標記 
			if(i%prime[j]==0)//如果是倍數 
			{
				t[i*prime[j]]=t[i]*prime[j]+e[i];
				e[i*prime[j]]=e[i];
				break;
			}
			else//不是倍數 
			{
				t[i*prime[j]]=t[i]*(prime[j]+1);
				e[i*prime[j]]=t[i];
			}
		}
	}
	for(int i=1;i<=n;i++)
		printf("%lld\n",t[i]);//輸出 
	return 0;
}

以上就是約數的內容,希望大家看後可以對約數有個理解,如果有不懂得歡迎留言。

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