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;
}
posted @ 2021-03-30 16:11  beatlesss  阅读(105)  评论(0编辑  收藏  举报