【题目链接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26999
【解题报告】
题意很简单,求LCM(1..N)。
N的范围为N<=1e8,样例组数T<=1e4.
要解决这道题目首先要知道一个结论:
我们设 g(n)=lcm(1,2,3,…,n)
如果 n=pk,g(n)=g(n−1)∗p.
否则 g(n)=g(n−1).
为什么结论成立呢?试着推了一下,如果推错了请提醒我改正
证明当N=P^k时,G(N)=G(N-1)*P:
设S={ P*1,P*2 ... P*(P-1),
P^2*1,P^2*2 ... P^2*(P-1),
P^3*1,P^3*2 ... P^2*(P-1),
...
P^(k-1)*1,P^(k-1)*2 ... P^(k-1)*(P-1) }
那么LCM(S) = LCM{ P*LCM(P-1),
P^2*LCM(P-1),
...
P^(k-1)*LCM(P-1) }
=P^(k-1)*LCM(P-1)
令G=(1...n-1),n=P^k.
设LCM(G-S)=T,
LCM(G-S,S)=P^(k-1)*LCM(T,LCM(P-1))
那么
LCM(n)=LCM(G-S,S,n)=LCM( P^k, P^(k-1)*LCM(T,LCM(P-1)) )
=P^k*LCM(T,LCM(P-1))
=P*LCM(G-S,S)
同理可证,当N=P^k*Q^m...时,G(N)=G(N-1);
题目空间给的很小,如何对内存进行优化可以参考下面的链接。
之后如何求LCM( 1..N )呢?
根据以上结论容易得出:
LCM(1..N)=P1^k1+P2^k2+P3^k3...+Pm^km
Pi为小于N的素数并且ki满足Pi^ki<=N<=Pi^(ki+1)
k1<=k2<=k3...<=km
如果我们直接利用这个式子来求LCM,需要每次求得Ki之后运行一次快速幂powmod(Pi,Ki),每一次powmod都需要logK的运算次数,由于本题时限卡的相当严格,所以可能会超时。(猜的,没写)
所以我们可以对上述等式继续化简,以期得到可以降低时间复杂度的运算式。
容易得出:
LCM(1..N)=Pm!+Pl!+Pk!...+Pj!(其中下标m>l>k>j)
于是我们可以预处理得到fac[i]表示素数P1*…*Pi,之后对于每次由后至前幂次增加的第一项Pi,O(1)计算一次fac(i)即可得到答案。
【参考资料】
(FreeWifi_novicer解题报告)
http://blog.youkuaiyun.com/qq_15714857/article/details/48879731
(ACdreamers解题报告)
http://blog.youkuaiyun.com/acdreamers/article/details/18364107
【参考代码】
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<functional>
#include<algorithm>
using namespace std;
const int N=1e8+5;
const int tag=(1<<5)-1;
typedef unsigned int uint;
int flag[N>>5];
int P[6000000+5];
uint sum[6000000+5];
uint ans;
int P_cnt=0;
inline void set_bit( int x )
{
flag[x>>5] |= (1<<(x&31)) ;
}
inline bool get_bit( int x )
{
return flag[x>>5] & (1<<(x&31));
}
void get_prime()
{
memset( flag,0,sizeof(flag) );
for( int i=2; i<N; i++ )
{
if(!get_bit(i))
{
P[++P_cnt]=i;
for( int j=i*2; j<N; j+=i )
{
set_bit(j);
}
}
}
}
void get_fac()
{
sum[0]=1;
for( int i=1; i<=P_cnt; i++ )
{
sum[i]=P[i]*sum[i-1]; //sum保存为uint,自动模2^32
}
}
int main()
{
int T,kase=0;
cin>>T;
get_prime();
get_fac();
while(T--)
{
int n;
scanf("%d",&n);
ans=1;
int cnt=1;
while(1)
{
int m=pow( n+0.9,1.0/cnt );
if(m<2)break;
int pos=lower_bound( P+1,P+P_cnt+1,m )-P;
if(P[pos]!=m)pos--;
ans*=sum[pos];
cnt++;
}
printf("Case %d: %u\n", ++kase, ans); //注意unsigned int输出是%u
}
return 0;
}

这道题目要求求解1到N的所有数的最小公倍数(LCM)。通过数学结论,可以简化计算过程。由于N的范围较大,样例组数较多,需要对内存进行优化。通过预处理素数乘积fac[i],并从前向后逐次计算,可以避免超时。参考解题报告和代码链接提供了详细的解题思路和实现方法。
19万+





