Light OJ 1289 LCM from 1 to n(lcm問題+內存優化)

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章