【框架編程思想】線數篩的高級應用(歐拉12題和歐拉21題)

題意:求解因子個數大於500的三角數

方法一:
約數個數定理:

時間複雜度:O(N^3)c++

#include <bits/stdc++.h>
using namespace std;

int64_t GetNumFac(int64_t x)
{
    int64_t ans = 1;
    //cout<< x ;
    for (int64_t i=2; i  <= x; i++)
    {
        if (x % i)continue;
        int times = 0;
        while (x % i == 0)
        {
            x  /= i;
            times ++;
        }
        ans *= (times + 1);
    }
    //cout<<"  **  "<<ans<<endl;
    return ans;
}
int main()
{
    int64_t n = 1;
    while (1)
    {
        if (n&1)
        {
            if (GetNumFac(n) * GetNumFac((n+1)/2) >= 500) break;
        }
        else
        {
            if(GetNumFac(n/2) * GetNumFac((n+1)) >= 500)  break;
        }
        n++;
    }
    printf("%lld\n",n*(n + 1 )/2);
    return 0;
}

方法二:
要點1:線性篩的應用——用24 和 2 標記48的約數個數
48 = 2^4 * 3^1 num[48] = (4+1)2=10
24 = 2^3
3^1 num[24] =(3+1)2= 8
模擬:
amt[48] = num[24] / (min_pow[i] + 1)
(min_pow[prime[j] * i] + 1);
amt[48] = num[24] / (min_pow[i] + 1) * (min_pow[i] + 2);
注意:prime[j] * i 和 i 的最小因子的冪次數必定相差,這個最小的因子必定是prime[j]
中心思想實現用 24 和 2 表示 48的因子個數
要點2:關於三角數的約數規律spa

時間複雜度:O(N)
代碼實現:code

#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6;

int amt[MAX_N+5] = {0};       ///約數個數
int prime[MAX_N + 5] = {0};   ///中間保存素數
int min_pow[MAX_N + 5] = {0}; /// 保存 最小素因子的冪次數+1

int Get_Trinum(int x)
{
    return x * ( x + 1 ) / 2;
}

void Init_NumOfPrime()
{
    for (int i=2; i <= MAX_N; i++)
    {
        if (!prime[i])
        {
            prime[++prime[0]] = i;
            amt[i] = 2;
            min_pow[i] = 1;
        }
        for (int j=1; j <= prime[0]; j++)
        {
            int tmp = i * prime[j];
            if (tmp > MAX_N) break;
            prime[tmp ] = 1;
            if (i % prime[j]) 
            {
                min_pow[ tmp ] =  1;
                amt[tmp] = amt[i] * amt[prime[j]];
            }
            else
            {
                min_pow[tmp ] = min_pow[i] + 1;
                amt[tmp ] = amt[i] / (min_pow[i] + 1 ) * (min_pow[tmp] + 1);
                break;///注意 : break緣由 :咱們這個題只須要更新最小素因子的冪次數,在不2 和 24 互素的狀況下,48最小素因子的冪次數已經更新了
            }
        }
    }
    return ;
}

int main()
{
    Init_NumOfPrime();
    int n = 1;
    while (1)
    {
        if (n&1)
        {
            if (amt[n] * amt[(n+1)/2] >= 500) break;
        }
        else
        {
            if(amt[n/2] * amt[n+1] >= 500) break;
        }
        n++;
    }
    printf("%d\n",Get_Trinum(n));
    return 0;
}

總結blog

變形應用:


題意:定義: a的約數和(不包括自己)等於b,b的約數和(不包括自己)等於a,a,b互爲基佬數,求10000之內的基佬數的和
要點:找出約數和與約數定理的關係簡化運算it

#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6;
const int N = 1e4;

int sum[MAX_N+5] = {0};       ///約數個數
int prime[MAX_N + 5] = {0};   ///中間保存素數
int min_pow[MAX_N + 5] = {0}; /// 保存 q^n

void Init_SumOfPrime()
{
    for (int i=2; i <= MAX_N; i++) ///注意:
    {
        if (!prime[i])
        {
            prime[++prime[0]] = i;
            sum[i] = 1 + i;
            min_pow[i] = i * i ;
        }
        for (int j=1; j <= prime[0]; j++)
        {
            int tmp = i * prime[j];
            if (tmp > MAX_N) break;
            prime[tmp ] = 1;
            if (i % prime[j])
            {
                min_pow[ tmp ] =  prime[j] * prime[j];
                sum[tmp] = sum[i] * sum[prime[j]];
            }
            else
            {
                min_pow[tmp] = min_pow[i] * prime[j];
                sum[tmp] = sum[i] * ( min_pow[tmp] - 1 ) / (min_pow[i] - 1 );
                break;
            }
        }
    }
    return ;
}

int main()
{
    Init_SumOfPrime();
    int ans = 0;
    for (int i = 2; i <= N; i++)
    {
        int num1 = sum[i] - i;
        int num2 = sum[num1] - num1;
        if (i == num2 && num1 <= N && num2 <= N && num1 != i)
        {
            ans += i;
        }
    }
    printf("%d\n",ans);
    return 0;
}
相關文章
相關標籤/搜索