我遇见谁会有怎样的对白
我等的人他在多远的未来
我听见风来自地铁和人海
我排着队拿着爱的号码牌
——孙燕姿《遇见》
题意:给出一个数S,求约数和等于S的数。
作为一道数学题,首先需要了解一些数学定理:
算数基本定理:
任何一个大于1的自然数N,都可以唯一分解成有限个质数的乘积N=P₁^a₁ P₂^a₂…Pn^an,这里P₁<P₂<…<Pn均为质数,其诸指数ai是正整数。
这样的分解称为N的标准分解式。
约数和定理:
对于任意一个大于1的正整数N可以分解正整数:N=P₁^a₁ P₂^a₂…Pn^an,则由约数个数定理可知N的正约数有(a₁+1)(a₂+1)(a₃+1)…(an+1)个,那么N的(a₁+1)(a₂+1)(a₃+1)…(an+1)个正约数的和为f(N)=(P₁^0+P₁^1+P₁^2+…P₁^a₁)(P₂^0+P₂^1+P₂^2+…P₂^a₂)…(Pn^0+Pn^1+Pn^2+…Pn^an)。
至此,搜索算法很显然地露出水面——穷举Pi及其对应的ai进行搜索。
①若当前数可表示成一个并未搜索过的质数与1的和,则之前搜索过的数与这个质数的乘积符合题意。
②对于每一个未被搜索过且平方小于当前数的质数,则枚举所有可能符合题意的ai进行递归搜索。
这样,此题便被完美解决。
【吐槽】当时JLOI时由于考场内由于台式机与投影仪的连接出现问题,评测机是一个临时借来的笔记本电脑——CPU慢得很恐怖,令我的卡时限的暴搜直接不解释地TLE……
顺便希望我所等待的那个人早日到来!!!
后来发生了一个惨(xi)绝(da)人(pu)寰(ben)的事情,这份代码被同屋的大神在时间上超越了……于是******,终于重夺 Rank 1~~时间仅用80ms~~
[JLOI2014]聪明的燕姿 C++代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 100000
int p[N+5],cnt,s,ans,num[N+5];
bool flag[N+5];
void getprime()
{
for(int i=2;i<=N;i++)
{
if(!flag[i]) p[++cnt]=i;
for(int j=1; i*p[j]<=N; j++)
{
flag[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
}
bool isprime(int x)
{
if(x==1) return false;
if(x<=N) return !flag[x];
for(int i=1;p[i]*p[i]<=x;i++)
if(x%p[i]==0) return false;
return true;
}
void dfs(int last,int now,int tot)
{
if(tot==1){ num[++ans]=now; return; }
if(tot-1>p[last]&&isprime(tot-1))
num[++ans]=now*(tot-1);
for(int i=last+1; p[i]*p[i]<=tot; i++)
for(int tnum=p[i]+1,t=p[i]; tnum<=tot; t*=p[i],tnum+=t)
if(tot%tnum==0)
dfs(i,now*t,tot/tnum);
}
int main()
{
getprime();
while(scanf("%d",&s)!=EOF)
{
ans=0;
dfs(0,1,s);
cout<<ans<<endl;
sort(num+1,num+ans+1);
for(int i=1; i<=ans; i++)
printf("%d%c",num[i],i==ans?'\n':' ');
}
}