Light 1289 - LCM from 1 to n (位图标记+素数筛选)

题目链接:

  http://www.lightoj.com/volume_showproblem.php?problem=1289

题目描述:

  给出一个n,求出lcm(1,2,3......n)为多少?

解题思路:

  lcm(1,2,3,......,n-1,n)等于所有小于n的素数p[i]的max(p[i]^k)相乘。

  暴力求解的话,由于时间的限制,很自然的想打了素数线性打标法,但是空间限制无法申请辣么大的标记数组。

  这个重要的时刻位图标记就闪亮登场啦!!!!

  int可以保存32位二进制,我们就可以把每一位当做一个数,又因为偶数除了二以外都不是素数,所以我们只需要筛选奇数。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef unsigned int UI;
 4 const int maxn = 100000005;
 5 const int N = 5800000;
 6 UI mul[N];
 7 int vis[maxn/32+10], p[N];
 8 int cnt, n;
 9 void init ()
10 {
11     cnt = 1;
12     p[0] = mul[0] = 2;
13     for (int i=3; i<maxn; i+=2)
14         if (!(vis[i/32]&(1<<((i/2)%16))))
15         {//寻找代表i的哪一位,偶数不占位数
16             p[cnt] = i;
17             mul[cnt] = mul[cnt-1] * i;
18             for (int j=3*i; j<maxn; j+=2*i)
19                 vis[j/32] |= (1<<((j/2)%16));//删除有因子的位数
20             cnt ++;
21         }
22         //printf ("%d\n", cnt);
23 }
24 UI solve ()
25 {
26     int pos = upper_bound(p, p+cnt, n) - p - 1;//找出最大的比n小的素数
27     UI ans = mul[pos];
28     for (int i=0; i<cnt&&p[i]*p[i]<=n; i++)
29     {
30         int tem = p[i];
31         int tt = p[i] * p[i];//这个tt很有可能溢出int(害的本宝宝wa了好几次)
32         while (tt/tem == p[i] && tt<=n)
33             {
34                 tem *= p[i];
35                 tt *= p[i];
36             }
37         ans *= tem / p[i];
38     }
39     return ans;
40 }
41 int main ()
42 {
43     int t, l = 0;
44     init ();
45     scanf ("%d", &t);
46     while (t --)
47     {
48         scanf ("%d", &n);
49         printf ("Case %d: %u\n", ++l, solve());
50     }
51     return 0;
52 }

 

posted @ 2015-07-16 21:35  罗茜  阅读(367)  评论(0编辑  收藏  举报