bzoj 3629: [JLOI2014]聪明的燕姿 (dfs+线性筛)

52 篇文章 1 订阅

3629: [JLOI2014]聪明的燕姿

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 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

HINT

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

Source

[ Submit][ Status][ Discuss]

题解: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]]);
	}
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值