1. 程式人生 > >BZOJ-1968-[Ahoi2005]COMMON 約數研究

BZOJ-1968-[Ahoi2005]COMMON 約數研究

ffffff names 有一種 上線 col pan spl min +=

Description

技術分享

Input

只有一行一個整數 N(0 < N < 1000000)。

Output

只有一行輸出,為整數M,即f(1)到f(N)的累加和。

Sample Input

3

Sample Output

5

題解

這道題剛開始以為是線性篩

但是有一種更優的算法

考慮因子中有因子i的數的數量

技術分享
 1 //考慮有多少個數是以i為因子的
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 int n,ans;
 5 int main(){
 6     scanf("
%d",&n); 7 for (int i=1;i<=n;i++) 8 ans+=n/i; 9 printf("%d\n",ans); 10 return 0; 11 }
View Code

這個代碼灰常短,也挺好理解的

Solution2:

但是如果想不到怎麽辦

那就只好強上線性篩了

這裏先說一下

num[i]表示i的因子個數,Min[i]表示i的最小質因子的次數

我們可以通過約數個數定理來求一個數的因子個數

傳送門:https://baike.so.com/doc/5806281-6019081.html

不難發現一個質數的因子個數為2(1和它本身),最小質因子的次數為1

我們在篩質數的時候判斷一下prime[j]是否為i的最小質因子

如果是

num[i*prime[j]]等於num[i]/(Min[i]+1)*(Min[i]+2)

Min[i*prime[j]]等於Min[i]+1 //最小質因子次數+1

如果不是

num[i*prime[j]]就等於num[i]*num[prime[j]] //符合積性函數

Min[i*prime[j]]等於1 //i*prime[j]的最小質因子為prime[j]且只有1次

技術分享
 1 //線性篩
 2 #include<bits/stdc++.h>
 3 #define N 1000005
 4 using
namespace std; 5 int n,ans,cnt; 6 int prime[N],num[N],Min[N]; 7 bool flag[N]; 8 int main(){ 9 scanf("%d",&n); 10 num[1]=1; 11 for (int i=2;i<=n;i++){ 12 if (!flag[i]){ 13 prime[++cnt]=i; 14 num[i]=2; 15 Min[i]=1; 16 } 17 for (int j=1;j<=cnt&&i*prime[j]<=n;j++){ 18 flag[i*prime[j]]=true; 19 if (!(i%prime[j])){ 20 num[i*prime[j]]=num[i]/(Min[i]+1)*(Min[i]+2); 21 Min[i*prime[j]]=Min[i]+1; 22 break; 23 } 24 num[i*prime[j]]=num[i]*num[prime[j]]; 25 Min[i*prime[j]]=1; 26 } 27 } 28 for (int i=1;i<=n;i++) 29 ans+=num[i]; 30 printf("%d\n",ans); 31 return 0; 32 }
View Code

BZOJ-1968-[Ahoi2005]COMMON 約數研究