Light OJ 1289 LCM from 1 to n(lcm问题+内存优化)

博客介绍了如何解决Light OJ 1289题目,即计算lcm(1,2,3,...,n)的问题。通过使用素数筛法和bitset优化空间复杂度,利用素数幂的性质减少存储,实现预处理O(nlogn)和查询O(n√)的时间复杂度。讨论了C/C++在内存操作上的优势,并提醒在处理大整数时注意模运算的使用,以及在空间紧张时避免使用STL容器。" 110779491,10294587,Python bytes类型数据split方法详解,"['Python编程', '数据处理', '字符串操作']

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

Light OJ 1289 LCM from 1 to n

题意:

输入 t(t10000) 组样例,
每组样例包含一个 n(2n108)
lcm(1,2,3,,n).

思路:

计算 lcm(1,2,3,,n)有这么一个性质:

我们设 g(n)=lcm(1,2,3,,n)
如果 n=pk,g(n)=g(n1)p.
否则 g(n)=g(n1).

由于多组样例直接求会TLE,如果预处理 g(n) 由于 n 过大,会导致MLE。

如何把握好时空上的取舍是这题的有趣之处,内存多给点会死啊!

首先要预处理 n 以内的素数,用一般筛法会用到 O(n)空间,这里我们可以用bitset来取代判断是否是素数的bool数组,将这里的空间复杂度进行了常数级优化。

由于 g(n) 的变化只与 n 为素数幂时有关,那么我们可以只存储 n 为素数幂时的 g(n) 可以节省很多空间,查询的时候二分即可。

按理来说以上步骤已经极度优化了复杂度,如果放codeforces早过了。。但依然是MLE 真是日了狗,大概还需要砍掉一半的空间。

刚才的算法时间复杂度是预处理 O(nlogn),一次查询 O(logn),显然查询复杂度比较小,可以支持我们把部分离线操作改为在线操作。

于是我们不存储任何 g(n) 了,只存储素数表与素数的前 n 项积,查询的时候二分 n 在素数表中的位置,得到比 n 小的最大素数 p 得到该素数的前n项积,再把 n之前的素数枚举一下它们比 n 小的素数幂,把结果相乘即可。
这样的时间复杂度为预处理 O(nlogn),一次查询 O(n),差不多。
主要空间大小有一个 108bit=1.25107byte 的素数判断数组,26106byte的素数表与同样大小的前n项积。加起来大概是 5107byte=5104Kb,差不多。

终于理解C/C++为啥大行于ACM,操作内存简直无情啊!

ps.这题要求要模除 232,其实就是一个unsigned int的大小,把与答案有关的操作中变量均改成unsigned int即可,溢出自动模除。

pss.原来我比较喜欢用vector来存储素数表,但这题这么做会MLE,vector动态更新大小条件是占用大概1.5倍乃至两倍内存,还是那句话。。时间空间卡太紧的时候慎用STL。。

代码:
/*
* @author FreeWifi_novicer
* language : C++/C
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<bitset>

using namespace std;

#define clr( x , y ) memset(x,y,sizeof(x))
#define cls( x ) memset(x,0,sizeof(x))
#define mp make_pair
#define pb push_back
typedef long long lint;
typedef long long ll;
typedef long long LL;
const int maxn = 1e8 + 5 ;
const int pnum = 5761460 ;
bitset<maxn>noprime ;
unsigned int mul[pnum] ;
int p[pnum] , len ;
int sieve(){
    noprime.reset() ;
    for( int i = 2 ; i * i <= maxn ; i++ ){
        if( !noprime[i] ){
            for( int j = i * i ; j <= maxn ; j += i ){
                noprime[j] = 1 ;
            }
        }
    }
    int k = 0 ;
    for( int i = 2 ; i <= maxn ; i++ )
        if( !noprime[i] ) p[k++] = i ;
    return k ;
}

void init_lcm(){
    len = sieve() ;
    mul[0] = p[0] ;
    for( int i = 1 ; i < len ; i++ )
        mul[i] = mul[i-1] * p[i] ;
}

void solve( int n ){
    int pos = lower_bound( p , p + len , n ) - p ;
    if( p[pos] != n ) pos-- ;
    unsigned int ans = mul[pos] ;
    for( int i = 0 ; p[i] * p[i] <= n ; i++ )
    {
        int tmp = p[i] ;
        int tmp2 = p[i] * p[i] ;
        while( tmp2 / tmp == p[i] && tmp2 <= n )
        {
            tmp = tmp * p[i] ;
            tmp2 = tmp2 * p[i] ;
        }
        ans = ans * ( tmp / p[i] ) ;
    }
    cout << ans << endl ;
}
int main(){
//  freopen("input.txt","r",stdin);
    int t ; cin >> t ; int kase = 1 ;
    init_lcm() ;
    while( t-- ){
        int n ;
        cin >> n ;
        printf( "Case %d: " , kase++ ) ;
        solve( n ) ;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值