题目链接:LCM from 1 to n
题意:求1 - n的lcm
思路:
- 最小公倍数的适用范围:分数的加减法,中国剩余定理(正确的题在最小公倍数内有解,有唯一的解)。因为,素数是不能被1和自身数以外的其它数整除的数;素数X的N次方,是只能被X的N及以下次方,1和自身数整除。所以,给最小公倍数下一个定义:S个数的最小公倍数,为这S个数中所含素因子的最高次方之间的乘积。
如:求756,4400,19845,9000的最小公倍数
因756=2 * 2 * 3 * 3 * 3 * 7,4400=2 * 2 * 2 * 2 * 5 * 5 * 11,19845=3 * 3 * 3 * 3 * 5 * 7 * 7,9000=2 * 2 * 2 * 3 * 3 * 5 * 5 * 5,这里有素数2,3,5,7,11.2最高为4次方16,3最高为4次方81,5最高为3次方125,7最高为2次方49,还有素数11。得最小公倍数为16 * 81 * 125 * 49 * 11=87318000.2,自然数1至50的最小公倍数,因为,√50≈7,所以,在50之内的数只有≤7的素数涉及N次方。在50之内,2的最高次方的数为32,3的最高次方的数为27,5的最高次方的数为25,7的最高次方的数为49,其余为50之内的素数。所以,1,2,3,4,5,6,…,50的最小公倍数为:32 * 27 * 25 * 49 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 *4 1 *4 3 * 47=3099044504245996706400(摘自百度百科) - 那么求 1 - n 的LCM就是求 <= n 的所有素数的最高次幂的乘积
如 LCM( 1 - 5 ) = 22 * 31 * 51 - 那么首先要筛素,直接开1e8的数组会超内存所以我们进行位图优化。
- 然后我们预处理出小于 n 的所有素数的乘积,
- 枚举所有小于 n 的素数,求他们的最高次幂
位图:
位图是一种优化空间的方法,我们知道在计算机中一个 int 是 32 个字节,那么,我们不妨用每一个字节表示是否存在一个数,那么一个 int 的位置就可以表示 32 个数。
比如vis[0] :0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0表示5存在
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#define lowbit(x) x & (-x)
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e8+10;
const ll mod = (ll)1 << 32;
int isp[N/32+50];
unsigned int mul[6000000];
int p[6000000], totp;
bool Get(int x)
{
int a = x / 32, b = x % 32;
return 1 << b & isp[a];
}
void Set(int x)
{
int a = x / 32, b = x % 32;
isp[a] |= (1 << b);
}
void getpri()
{
Set(0);
Set(1);
for(int i = 2; i < 1e8; i++)
{
if(!Get(i))
{
p[totp++] = i;
if(i > N / i) continue;
for(int j = i * i; j < 1e8; j += i) Set(j);
}
}
mul[0] = p[0];
for(int i = 1; i < totp; i++) mul[i] = (ll)mul[i-1] * p[i] % mod;//求素数前缀积
}
unsigned int getans(int n)
{
unsigned int ret = 0;
int last = upper_bound(p , p + totp, n) - p - 1;//第一个大于 n 的位置减一就是最接近
ret = mul[last]; //n 的素数,然后直接用前缀积
for(int i = 0; i < totp && p[i] * p[i] <= n; i++)
{
int now = p[i];
int tmp = p[i] * p[i];
while(tmp / now == p[i] && tmp <= n)//两个判断条件一个都不能少, 前面条件的是为
{ //了防止超爆 int
tmp *= p[i];
now *= p[i];
}
ret *= (now / p[i]);
}
return ret;
}
int main()
{
getpri();
int t, n;
scanf("%d", &t);
for(int ca = 1; ca <= t; ca++)
{
scanf("%d", &n);
printf("Case %d: %u\n", ca, getans(n));
}
return 0;
}