LightOJ 1289 LCM from 1 to n (节省空间的素数筛法+n个数的最小公倍数)

题目链接:http://lightoj.com/volume_showproblem.php?problem=1289

题意:给出n,求1到n的所有数的最小公倍数,模2^32.

思路:看到结果需要模2^32,瞬间想到要尝试用unsigned int来存储结果,它可以表示的数据范围是0~2^32-1,这样不用做任何操作,结果自然就是模2^32的。

接下来就是筛素数了,常用的方法学名是:埃拉托斯特尼筛法,这里有个不错的图来表示(貌似csdn不支持gif了???)

LightOJ 1289 LCM from 1 to n (节省空间的素数筛法+n个数的最小公倍数)_第1张图片

贴一个我以前一直在用的实现:

void Prime ()           //素数打表prime数组从1开始
{
    for (int i=2;i<10005;i++) if (!visit[i])
    {
		prime[++Num_Prime]=i;
        for (int j=i+i;j

说明:第二重循环可以改成 int j=i*i; 因为 对于一个数x,假设它含有质因子i,那么令y=x/i;可以发现,如果所有小于i*i的含有因子i的数字,其y值小于i,在以前的筛选过程中,就会把x筛掉,所以没有必要重新筛选一遍。但是要注意两个int相乘有可能超范围……


这个题貌似用通常的筛法会出现爆内存的情况(我没测),最近学习了一个节省空间的素数筛法,按照目前我的写法visit数组可以减到原来的32分之一。貌似还能优化……

用到了一个叫位图的数据存储方法,理论:

数据结构之位图 | 董的博客

素数判定算法 | 董的博客

可以参考的一个实现:节约空间的筛素数方法 - raomeng1的专栏 - 博客频道 - CSDN.NET

我的实现详见最下面的代码,参考了vjudge上的一份共享的代码。

记录几个常用的数据,摘自http://blog.sina.com.cn/s/blog_484bf71d0100ok5a.html

一百以内有 25 个素数,它们分别是 

2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97。
一千以内有 168 个素数,最后几个是 

907,911,919,929,937,941,947,953,967,971,977,983,991,997。
一万以内有 1229 个素数,最后几个是 

9901,9907,9923,9929,9931,9941,9949,9967,9973。
十万以内有 9592 个素数,最后几个是 

99901,99907,99923,99929,99961,99971,99989,99991。
一百万以内有 78498 个素数,最后几个是 

999907,999917,999931,999953,999959,999961,999979,999983。
一千万以内有 664579 个素数,最后几个是 

9999901,9999907,9999929,9999931,9999937,9999943,9999971,9999973,9999991。
一亿以内有 5761455 个素数,最后几个是 

99999931,99999941,99999959,99999971,99999989。
十亿以内有 50847534 个素数,最后几个是 

999999929,999999937。
一百亿以内有 455052511 个素数,最后几个是 

9999999929,9999999943,9999999967。


下面说一下这个题的思路,直接模拟肯定超时,用记录每个素因子最大个数的方法写了两次都超时……应该是我写的比较挫……有时间再试下。

比较好的思路是把主要的数据先都预处理出来。

以下过程参考了原题的discuss。

定义L(x)为 1, 2, 3, .., x的LCM

则有如下规律:

L(1) = 1

L(x+1) = { L(x) * p    if x+1 is a perfect power of prime p
         { L(x)        otherwise
也就是当x+1是素数p的整数次幂的时候,L(x+1)=L(x)*p;举例如下:

L(2) = 1 * 2
L(3) = 1 * 2 * 3
L(4) = 1 * 2 * 3 * 2      // because 4 = 2^2
L(5) = 1 * 2 * 3 * 2 * 5
L(6) = 1 * 2 * 3 * 2 * 5  // 6 is not a perfect power of a prime
L(7) = 1 * 2 * 3 * 2 * 5 * 7

于是我们可以先把素数连乘的结果预处理出来,然后再对每一个素数的整数次幂根据n的不同进行操作。

#include 
#include 
using namespace std;

const int N=100000007;

int visit[N/32+50];
unsigned int data[5800000];
int prime[5800000],np=0;

void Prime ()   //筛素数,数组从0开始
{
	prime[0]=data[0]=2;
	np=1;
	for (int i=3;i


你可能感兴趣的:(LightOJ,数学)