【BZOJ 3629】【JLOI 2014】聪明的燕姿【约束和+dfs】

Description

阴天傍晚车窗外
未来有一个人在等待
向左向右向前看
爱要拐几个弯才来
我遇见谁会有怎样的对白
我等的人他在多远的未来
我听见风来自地铁和人海
我排着队拿着爱的号码牌
城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于S。
所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。

Input

输入包含k组数据(k<=100)对于每组数据,输入包含一个号码牌S

Output

对于每组数据,输出有两行,第一行包含一个整数m,表示有m个等的人,第二行包含相应的m个数,表示所有等的人的号码牌。注意:你输出的号码牌必须按照升序排列。

Sample Input

42

Sample Output

3
20 26 41

HINT

对于100%的数据,有S<=2*10*9

题解

对于一个大于1正整数n可以分解质因数:n=p1^a1*p2^a2*p3^a3*…*pk^ak,
则由约数个数定理可知n的正约数有(a₁+1)(a₂+1)(a₃+1)…(ak+1)个,
那么n的(a₁+1)(a₂+1)(a₃+1)…(ak+1)个正约数的和为
f(n)=(p1^0+p1^1+p1^2+…p1^a1)(p2^0+p2^1+p2^2+…p2^a2)…(pk^0+pk^1+pk^2+…pk^ak)

由此可见,这题就是一个搜索题
可怎么搜索呢?

穷举Pi及其对应的ai进行搜索。

  1. 若当前数可表示成一个并未搜索过的质数与1的和,则之前搜索过的数与这个质数的乘积符合题意。
  2. 对于每一个未被搜索过且平方小于当前数的质数,则枚举所有可能符合题意的ai进行递归搜索。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long ll;
#define N 50000
int s,sqrts,top,cnt;
int p[N+10],ans[N * 2];
bool vis[N+10];

void getpri()
{
    memset(vis,1,sizeof(vis));
    vis[1] = 0;
    for(int i = 2;i <= N;i++)
    {
        if(vis[i]) p[++cnt] = i;
        for(int j = 1;p[j] * i <= N;j++)
        {
            vis[p[j] * i] = 0;
            if(i % p[j] == 0) break;
        }
    }
}

bool jud(int x)
{
    if(x <= N) return vis[x];
    else
    {
        int t = sqrt(x);
        for(int i = 1;p[i] <= t;i++)
            if(x % p[i] == 0) return 0;
        return 1;
    }
}

void dfs(int last,ll tot,int sum)
{
    if(tot == 1){ans[++top] = sum;return;}
    if(tot - 1 > sqrts && jud(tot-1)) ans[++top] = sum*(tot-1);
    for(int i = last+1;p[i] <= sqrts;i++)
    {
        ll tmp = 1,t = p[i];
        for(int j = 1;tmp + t <= tot;j++)
        {
            tmp += t;
            if(tot % tmp == 0) dfs(i,tot/tmp,sum*t);
            t *= p[i];
        }
    }
}

int main()
{
    getpri();
    while(scanf("%d",&s) != EOF)
    {
        top = 0; sqrts = sqrt(s);
        dfs(0,s,1);
        printf("%d\n",top);
        sort(ans+1,ans+top+1);
        for(int i = 1;i < top;i++)
            printf("%d ",ans[i]);
        if(top) printf("%d\n",ans[top]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值