Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=4133
【前言】
一拿到题就想到可以打表。
虽然上限是20亿,但是我觉得真正的good number应该不多。
于是哗啦啦开始暴力地打表。
但是很快就发现不行了。
暴力打表肯定打不出来。
然后想到可以用素数去筛。
但是20亿的素数?
上网找了个神马1亿以内素数3秒内出的代码,居然把256MB内存的机器跑死了。
无奈。
昨天晚上开始找规律。一路坎坷地走过来。
最后慢慢摸索出来了。
交了一份打表的。15ms。
然后交了一份没有打表的。也是15ms。
看来数据还是很弱的。
【思路】
首先,根据素数找出这些素数所能组成的相同约数的最小数。
很拗口的说。
首先确定某个数肯定是由素数组成的。
然后枚举每个素数,同时枚举相同素数的不同个数,计算出小于20亿的数。
与此同时,计算出它的约数个数。
如果已知素数及其个数,那约数个数就不难求了。
比如某个数包含的质因子为Pi,对应的质因子的个数为Qi,那么约数个数就是(Q1+1)*(Q2+1)*...*(Qn+1)。
然后对于相同的约数个数,记录其最小值。
最后得到的一组数就是从小到大所对应的最小数。
然后再扫描一遍这个数组,去掉那些约数个数比前面小的数。
最终得到了20亿以内所有good number。
然后对于每个输入的数,在good number中进行二分查找即可。
这道题可以按上面那种运算得到数组然后打表,或者不打表直接计算也可以。
关键是,如果真的枚举所有素数,那么肯定出不来结果的。
通过观察发现,如果约数个数一定,那么肯定是小数相乘得到的数更小。
所以,可以知道,只要小范围内的质数就可以满足要求了。
这里只需要大概八个最小的质数就可以满足20亿以内的good number。
这八个质数保存在prime[ ]中。
同时可以事先记录其所需要的最大个数,保存在ct[ ]中。
【代码】
非打表
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
int getdiv(int n)//求约数个数
{
if (n==1) return 1;
int i;
int s = 2;
int m = (int)sqrt((double)n);
for (i=1; i<=m; i++)
{
if (n%i==0) s+=2;
}
if (m*m==n) s--;
return s;
}
const __int64 maxn = 2000000000;
vector<__int64>v, vt;
__int64 prime[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int ct[8] = {31, 20, 14, 12, 9, 9, 8, 8};
map<int, __int64>hash;//哈希相同约数的最小值
map<int, __int64>::iterator it;
void dfs(int x, int n, int k, __int64 s)
{
if (x==n) return;
int i;
__int64 t = s;
for (i=1; i<ct[x]; i++)
{
t *= prime[x];
if (t<=maxn)
{
if (hash.find(k*(i+1))==hash.end()) hash[k*(i+1)] = t;
else if (t<hash[k*(i+1)]) hash[k*(i+1)] = t;
dfs(x+1, n, k*(i+1), t);
}
else break;
}
}
bool cmp(const __int64 &a, const __int64 &b)
{
return a<b;
}
void init()
{
hash.clear();
hash[1] = 1;
dfs(0, 8, 1, (__int64)1);
v.clear();
int i,j;
for (it=hash.begin(); it!=hash.end(); it++)
v.push_back((*it).second);
sort(v.begin(), v.end(), cmp);
vt.clear();
vt.push_back(1);
for (i=1; i<v.size(); i++) vt.push_back(getdiv(v[i]));
for (i=1,j=0; i<vt.size(); i++)
{
if (vt[i]>vt[j])
{
j++;
vt[j] = vt[i];
v[j] = v[i];
}
}
}
__int64 bs(int s, int t, __int64 x)
{
int left, right, mid;
__int64 ans;
left = s-1;
right = t;
while(left<=right)
{
mid = (left+right)>>1;
if (x>=v[mid])
{
ans = v[mid];
left = mid+1;
}
else right = mid-1;
}
return ans;
}
int main()
{
int t, i;
__int64 n;
init();
scanf("%d", &t);
for (i=1; i<=t; i++)
{
scanf("%I64d", &n);
printf("Case #%d: %I64d\n", i, bs(0, 67, n));
}
return 0;
}
打表
#include <iostream>
using namespace std;
__int64 v[70] = {1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,
15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,
554400,665280,720720,1081080,1441440,2162160,2882880,3603600,4324320,6486480,7207200,8648640,
10810800,14414400,17297280,21621600,32432400,36756720,43243200,61261200,73513440,110270160,
122522400,147026880,183783600,245044800,294053760,367567200,551350800,698377680,735134400,
1102701600,1396755360};
__int64 bs(int s, int t, __int64 x)
{
int left, right, mid;
__int64 ans;
left = s-1;
right = t;
while(left<=right)
{
mid = (left+right)>>1;
if (x>=v[mid])
{
ans = v[mid];
left = mid+1;
}
else right = mid-1;
}
return ans;
}
int main()
{
int t,i;
__int64 n;
scanf("%d", &t);
for (i=1; i<=t; i++)
{
scanf("%I64d", &n);
printf("Case #%d: %I64d\n", i, bs(0, 67, n));
}
return 0;
}

本文探讨了解决特定大数分解问题的优化策略与代码实现细节,从暴力打表到利用素数筛法,再到发现规律进行高效计算。详细介绍了算法逻辑、关键步骤以及代码优化技巧,最终提供了两种实现方式:打表和非打表。通过对比分析数据,揭示了数据集的弱点,并给出了实例代码。
380

被折叠的 条评论
为什么被折叠?



