一.题目描述

二.初始代码
问题:时间超限
cs
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
int s(int n)//数码和
{
int num = 0;
while (n)
{
num += n % 10;
n /= 10;
}
return num;
}
bool check(int n)
{
for (int i = 2;i * i <= n;i++)
{
if (n % i != 0)
continue;
int x = i, y = n / i;
if (s(n) == s(x) * s(y))
return true;
}
return false;
}
bool N[10000010] = { false };
void Sum()
{
for (int i = 1;i <= 10000000;i++)
{
if (check(i))
N[i] = true;
}
return;
}
int main()
{
int t;scanf("%d", &t);
Sum();
while (t--)
{
int L, R;
scanf("%d %d", &L, &R);
int cnt = 0;
for (int i = L;i <= R;i++)
{
if (N[i])
cnt++;
}
printf("%d\n", cnt);
}
return 0;
}
三.反思问题
考虑到可以在遍历样例前将所有可能的n记录下来,但忽略了每个数值都遍历寻找因数的代价过大(因子问题可以考虑用埃式筛法解决),没有考虑到可以运用前缀和的方法存储个数提高效率,也没有考虑到可以提前用数组存储各数值数码和提高效率.......
关键优化点总结:
-
避免对每个数单独分解因子,而是通过双重循环生成所有可能的乘积
-
提前计算数码和,避免重复计算
-
使用前缀和快速回答区间查询
-
利用乘积对称性减少循环次数
最终AC代码
cs
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
//全局变量,分配在数据段,不会溢出
int s[10000010] = { 0 };
bool N[10000010] = { false };
int pre[10000010] = { 0 };
int main()
{
int t;scanf("%d", &t);
//数码和可以预处理,用数组存储提高效率
for (int i = 1;i <= 1e7;i++)
{
s[i] = s[i / 10] + (i % 10);
}
//结合埃式筛选法遍历找出满足"数码积性"的n(素数,合数问题)
for (int i = 2;i <= 1e7;i++)
{
if (!N[i])
{
for (int j = i;(long long)j * i <= 1e7;j++)
{
int n = j * i;
if (s[n] == s[j] * s[i])
N[n] = true;
}
}
}
//区间个数运用前缀和
for (int i = 4;i <= 1e7;i++)
{
pre[i] = pre[i - 1] +( N[i] ? 1 : 0);
}
while (t--)
{
int L, R;
scanf("%d %d", &L, &R);
printf("%d\n", pre[R] - pre[L - 1]);
}
return 0;
}