【JSOI 2014】聪明的燕姿

更好的阅读体验

传送门

【题目背景】

阴天傍晚车窗外
未来有一个人在等待
向左向右向前看
爱要拐几个弯才来
我遇见谁会有怎样的对白
我等的人他在多远的未来
我听见风来自地铁和人海
我排着队拿着爱的号码牌

【题目描述】

城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。

可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于 S。

所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。

输入格式:

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

输出格式:

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

【Input】

42

【Output】

3
20 26 41

【分析】

看了不少大佬的题解,这里写一下蒟蒻自己的理解,至于代码,其实和各位大佬的差不多。
首先,这里要用到惟一分解定理:

\[n=p_1^{c_1}*p_2^{c_2}*……*p_k^{c_k} \]

还有约数和:

\[sum=\prod_{i=1}^{n}{\sum_{j=0}^{k}{p_i^j}} \]

这些应该没什么好说的。注意,如果\(s-1\)为质数,则\(s-1\)也是一个号码牌,所以这里需要特判。搜索中的\(now\)相当于搜寻到的质数的乘积,\(last\)为搜索的质数的下标,\(left\)为剩下的\(s\)

【代码】

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define maxn 60000
using namespace std;
int s;
int cnt=0,num;
int prime[maxn],ans[maxn];
bool flag[maxn];
void primes(){
	memset(flag,1,sizeof(flag));//筛素数
	flag[1]=0;
	for(int i=2;i<=maxn;i++){
		if(!flag[i]) continue;
		else{
			prime[++cnt]=i;
			for(int j=2;j*i<=maxn;j++)
				flag[i*j]=0;
		}
	}
}
bool is_prime(int x){//判断是否为素数
	if(x==1) return false;
	if(x==2) return true;
	for(int i=1;prime[i]*prime[i]<=x;i++)
		if(x%prime[i]==0) return false;
	return true;
}
void dfs(int now,int last,int left){//搜索
	if(left==1) {ans[++num]=now;return;}
	if(left-1>prime[last]&&is_prime(left-1)) ans[++num]=now*(left-1);
	for(int i=last+1;prime[i]*prime[i]<=left;i++)
		for(int j=prime[i]+1,k=prime[i];j<=left;k*=prime[i],j+=k)
			if(left%j==0)
				dfs(now*k,i,left/j);
}
int main(){
	primes();
	while(scanf("%d",&s)!=EOF){
		num=0;
		memset(ans,0,sizeof(ans));
		dfs(1,0,s);
		sort(ans+1,ans+1+num);//号码牌要按升序排列
		printf("%d\n",num);
		for(int i=1;i<=num;i++){
			printf("%d",ans[i]);
			if(i==num) printf("\n");
			else printf(" ");
		}
	}
	return 0;
}

posted @ 2019-07-02 11:10  小蒟蒻hlw  阅读(149)  评论(0)    收藏  举报