【BZOJ3629】[JLOI2014]聰明的燕姿 數學+搜索

我遇見誰會有怎樣的對白
我等的人他在多遠的未來
我聽見風來自地鐵和人海
我排着隊拿着愛的號碼牌
——孫燕姿《遇見》
題意:給出一個數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……
順便希望我所等待的那個人早日到來!!!
注:時至今日,我的代碼仍然處於 BZOJ 榜首(100MS&108MS)且 Code_Length 1111 B!
後來發生了一個慘(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':' ');
	}
}


發佈了25 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章