【框架编程思想】线数筛的高级应用(欧拉12题和欧拉21题)

题意:求解因子个数大于500的三角数

方法一:
约数个数定理:

时间复杂度:O(N^3)

#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:关于三角数的约数规律

时间复杂度:O(N)
代码实现:

#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;
}

总结

变形应用:


题意:定义: a的约数和(不包括本身)等于b,b的约数和(不包括本身)等于a,a,b互为基佬数,求10000以内的基佬数的和
要点:找出约数和与约数定理的关系简化运算

#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;
}
posted @ 2017-11-13 01:33  799  阅读(256)  评论(0编辑  收藏  举报