题目描述
分析
提到约数之和,就不难想到约数个数和约数之和的两个公式
对于这道题来说,如果这个数大于S,那么这个数必然有两个因子,一个1一个S,那么这个数一定不符合要求,因此只需要在1~S中考虑即可
假设X分解出的每一个素因子都是230=1 073 741 824,因此每个素因子的次幂都不会太多
假设X分解出的每个素因子都只出现1次,且出现的都是最小的,仅仅考虑前15个素数求素数和的答案是1 610 612 736,每个因子分解出来的素数不会太多
两者结合起来,X分解出来的素因子和次幂数就更不会很大了!
因此可以直接暴搜,搜出所有的素因子和次幂就可以啦
首先规定搜索的顺序,默认枚举的素因子是严格单调递增的
如果我们要直接枚举所有可能的质数的话,就需要枚举大概1e8左右的数,很显然这是不行的,需要想一些剪枝来优化搜索的过程
对于每一个当前剩余的S,都判断S是否等于a(质数)+1
如果满足,则不用再进行下面的枚举
如果不满足,则有一下两种情况
不论是对于哪一种情况,都必然会有pi2<S,因此我们只需要枚举到S1/2即可,这样复杂度就大大降低了
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 45000;
int prime[N], cnt;
bool st[N];
int ans[N], len;
void get_prime(int n)
{
for (int i = 2; i <= n; i++)
{
if (!st[i])
prime[cnt++] = i;
for (int j = 0; prime[j] * i <= n; j++)
{
st[prime[j] * i] = 1;
if (i % prime[j] == 0)
break;
}
}
}
bool check(int n)
{
if (n < N)
return !st[n];
for (int i = 0; prime[i] <= n / prime[i]; i++)
if (n % prime[i] == 0)
return 0;
return 1;
}
//last表示当前用到的素数的下标是多少
//res表示当前的答案是多少
//s表示当前剩余的S是多少
void dfs(int last, int res, int s)
{
if (s == 1) //表示这个数已经被完美的凑出来了
{
ans[len++] = res;
return;
}
if (s - 1 > (last < 0 ? 1 : prime[last]) && check(s - 1))
ans[len++] = res * (s - 1);
for (int i = last + 1; prime[i] <= s / prime[i]; i++)
{
int temp = prime[i];
for (int j = 1 + temp, t = temp; j <= s; t *= temp, j += t) //j保存的是和,t保存的是最后一个次幂的值
if (s % j == 0)
dfs(i, res * t, s / j);
}
}
int main()
{
get_prime(N - 1);
int s;
while (cin >> s)
{
len = 0;
dfs(-1, 1, s);
cout << len << endl;
if (len)
{
sort(ans, ans + len);
for (int i = 0; i < len; i++)
cout << ans[i] << " ";
cout << endl;
}
}
return 0;
}