HDU4133 StrangeStandard 打表?

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

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;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值