【框架编程思想】线数筛的高级应用(欧拉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;
}