题目链接: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了???)
贴一个我以前一直在用的实现:
void Prime () //素数打表prime数组从1开始
{
for (int i=2;i<10005;i++) if (!visit[i])
{
prime[++Num_Prime]=i;
for (int j=i+i;j
这个题貌似用通常的筛法会出现爆内存的情况(我没测),最近学习了一个节省空间的素数筛法,按照目前我的写法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