3629: [JLOI2014]聪明的燕姿
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1135 Solved: 406
[ Submit][ Status][ Discuss]
Description
阴天傍晚车窗外
未来有一个人在等待
向左向右向前看
爱要拐几个弯才来
我遇见谁会有怎样的对白
我等的人他在多远的未来
我听见风来自地铁和人海
我排着队拿着爱的号码牌
城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于S。
所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。
Input
输入包含k组数据(k<=100)
对于每组数据,输入包含一个号码牌S
Output
对于每组数据,输出有两行,第一行包含一个整数m,表示有m个等的人,第二行包含相应的m个数,表示所有等的人的号码牌。注意:你输出的号码牌必须按照升序排列。
Sample Input
42
Sample Output
3
20 26 41
20 26 41
HINT
对于100%的数据,有S<=2*10*9
Source
题解:dfs+线性筛
需要用到约数和公式:F(n)=∏(1<=i<=k)[pi^(qi+1)-1)/(pi-1)]
然后对于sqrt(s)以内的素数枚举,大于sqrt(s)之间判断,dfs的时候加一些减值和特判即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100003
#define LL long long
using namespace std;
int prime[N],pd[N],n,ans[N],cnt,m;
void init()
{
for (int i=2;i<=100000;i++) {
if (!pd[i]) prime[++prime[0]]=i;
for (int j=1;j<=prime[0];j++) {
if (prime[j]*i>100000) break;
pd[prime[j]*i]=1;
if (i%prime[j]==0) break;
}
}
}
bool check(int x)
{
for (int i=2;i*i<=x;i++)
if (x%i==0) return false;
return true;
}
void dfs(int x,int sum,int num)
{
if (sum>m&&check(sum-1))
ans[++ans[0]]=num*(sum-1);
if (sum<0) return;
if (sum==1) {
ans[++ans[0]]=num;
return;
}
int t=num; LL sum1=sum;
for (int i=x;i<=prime[0];i++) {
if (sum<prime[i]) break;
LL pri=prime[i];
while (true) {
sum1=((LL)pri*prime[i]-1)/(LL)(prime[i]-1);
if (sum%sum1==0) dfs(i+1,sum/sum1,num*pri);
if (sum/sum1<prime[i]) break;
pri*=(LL)prime[i];
}
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
init();
while (scanf("%d",&n)!=EOF) {
cnt=0; ans[0]=0; m=ceil(sqrt(n));
dfs(1,n,1);
sort(ans+1,ans+ans[0]+1);
ans[0]=unique(ans+1,ans+ans[0]+1)-ans-1;
printf("%d\n",ans[0]);
for (int i=1;i<ans[0];i++) printf("%d ",ans[i]);
if(ans[0]) printf("%d\n",ans[ans[0]]);
}
}