Light OJ 1289 LCM from 1 to n
题意:
输入 t(t≤10000) 组样例,
每组样例包含一个 n(2≤n≤108),
求 lcm(1,2,3,…,n).
思路:
计算 lcm(1,2,3,…,n)有这么一个性质:
我们设 g(n)=lcm(1,2,3,…,n)
如果 n=pk,g(n)=g(n−1)∗p.
否则 g(n)=g(n−1).
由于多组样例直接求会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项积,再把
这样的时间复杂度为预处理 O(nlogn),一次查询 O(n√),差不多。
主要空间大小有一个 108bit=1.25∗107byte 的素数判断数组,2∗6∗106byte的素数表与同样大小的前n项积。加起来大概是 5∗107byte=5∗104Kb,差不多。
终于理解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;
}