A - LCM from 1 to n (欧拉筛 + 快速幂 + LCM )

博客介绍了如何利用欧拉筛和快速幂算法求解1到n的最小公倍数(LCM)。通过预处理素数和考虑素数的整数次幂,有效计算大规模数的LCM。示例展示了不同n值下的结果,并给出了详细的计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given an integer n, you have to find

lcm(1, 2, 3, ..., n)

lcm means least common multiple. For example lcm(2, 5, 4) = 20, lcm(3, 9) = 9, lcm(6, 8, 12) = 24.

Input

Input starts with an integer T (≤ 10000), denoting the number of test cases.

Each case starts with a line containing an integer n (2 ≤ n ≤ 108).

Output

For each case, print the case number and lcm(1, 2, 3, ..., n). As the result can be very big, print the result modulo 232.

Sample Input

5

10

5

200

15

20

Sample Output

Case 1: 2520

Case 2: 60

Case 3: 2300527488

Case 4: 360360

Case 5: 232792560

题目大意:给出几个n,求出1到n的最小公倍数

思路概括:

1、埃拉托斯特尼筛法,这里有个不错的图来表示

一百以内有 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。
比较好的思路是把主要的数据先都预处理出来。

 

定义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<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define lt k<<1
#define rt k<<1|1
#define lowbit(x) x&(-x)
#define lson l,mid,lt
#define rson mid+1,r,rt
using namespace std;
typedef long long  ll;
typedef long double ld;
typedef unsigned int uint;
typedef unsigned long long ull;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define mem(a, b) memset(a, b, sizeof(a))
//#define int ll
const double pi = acos(-1.0);
const double eps = 1e-6;
const double C = 0.57721566490153286060651209;
const ll mod = 1ll << 32;
const int inf = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const uint INF = 0xffffffff;
const int maxn = 1e8 + 5;
unsigned int pri[5800000];
unsigned int sum[5800000];
bitset<maxn> vis;
int cnt = 1;
void prime()//欧拉筛
{
    pri[0] = 0;
    for(int i=2; i<maxn; i++)
    {
        if(!vis[i]) pri[cnt++] = i;
        for(int j=1; j<cnt && i * pri[j] < maxn; j++)
        {
            vis[i * pri[j]] = true;
            if(i % pri[j] == 0) break;
        }
    }
    sum[0] = 1;
    for(int i=1; i<cnt; i++)//前缀积
    {
        sum[i] = sum[i-1] * pri[i];
    }
}
ll q_mod(int a, int b)//快速幂
{
    ll ans = 1;
    ll base = a;
    while(b)
    {
        if(b & 1) ans = (ans * base) % mod;
        base = (base * base) % mod;
        b >>= 1;
    }
    return ans;
}
int main()
{
    prime();
    int t;
    cin >> t;
    int k = 1;
    while(t--)
    {
        int n;
        cin >> n;
        int pos = upper_bound(pri+1, pri+cnt, n) - pri - 1;//二分查找第一个比n大的数,然后减去1,就小于等于n
        ll ans = sum[pos] % mod;
        for(int i=1;i<cnt && pri[i] * pri[i] <= n;i++)
        {
            int x = n;
            int y = 0;
            while(x/pri[i])//每个素数的最大次方
            {
                x /= pri[i];
                y++;
            }
            ans = q_mod(pri[i], --y) * ans % mod;
        }
        cout << "Case " << k++ << ": ";
        cout << ans % mod << endl;
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值