AcWing 1296. 聪明的燕姿
原题链接
思路
算术基本定理:任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积
则可以对S进行分解质因数 \(S = p1^{a1} + p2^{a2} + ... + pn^{an}\)
根据约数之和 \(sum = (1 + p1^1 + p1^2 + ... + p1^{a1}) * ... * (1 + pn^1 + ... + pn^{an})\)
而任何约数之和为S的数一定可以表示为约束之和公式的形式, 即\(S = sum\)
因此除了1 + p这种形式之外, p一定小于\(\sqrt{S}\), 因此筛质数只需要筛到\(\sqrt{S}\)就行
容易判断,乘的项不会超过十项,因此可以采用暴搜的方法枚举出所有满足约数之和为S的数。
暴搜时,保证p是递增的,避免重复搜索。
注意:相对于约数之和公式,所求的答案是\(ans = p1^{a1} * ... * pk^{ak}\)
AC代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 5e4 + 10;
int primes[N], cnt;
bool st[N];
int ans[N], len;
void get(int n) //线性筛
{
for(int i = 2 ; i <= n ; i ++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = 0 ; primes[j] * i <= n ; j ++)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
bool judge(int x) //试除法判断x是否是质数
{
if(x < N) return !st[x];
for(int i = 2 ; i <= x / i ; i ++)
if(x % i == 0) return false;
return true;
}
void dfs(int last, int sum, int s) //暴搜 last是上次枚举的质数下标 sum是当前让剩下项乘积为s的数的数值
{
if(s == 1)
{
ans[len ++] = sum;
return;
}
if(s - 1 > (last >= 0 ? primes[last] : 1) && judge(s - 1)) ans[len ++] = sum * (s - 1);
for(int i = last + 1 ; primes[i] <= s / primes[i] ; i ++)
{
int p = primes[i];
for(int j = 1 + p, t = p ; j <= s ; t *= p, j += t)
if(s % j == 0)
dfs(i, sum * t, s / j);
}
}
int main()
{
int x;
get(N - 1);
while(cin >> x)
{
len = 0;
dfs(-1, 1, x);
sort(ans, ans + len);
cout << len << endl;
if(len)
{
for(int i = 0 ; i < len ; i ++) cout << ans[i] << ' ';
cout << endl;
}
}
return 0;
}