LightOJ 1289 - LCM from 1 to n
You are given T
test cases, for each test case you are given n
.
You have to find lcm(1, 2,...,n)
where lcm refers to the least common multiple of some integers, i.e. the smallest integer that is a multiple of all of the given integers (modulo 2^32).
Summary
lcm(b1, b2, ... , bm) can be represented as p1a1 . p2a2 . ... . pkak where p1, p2, ... , pk are prime factors of those numbers and a1, a2, ... , ak are the maximum powers of those corresponding primes, that occur in the numbers b1, b2, ... , bk . This number theory problem requires you to find the prime factors and their corresponding powers and the product of these.
In this problem, you can assume b1, b2, ... , bm = 1,2, ... , n .
Solution
Naive Approach
Let's calculate for each prime (until n) the maximum power a of this prime p, so that pa <= n. For example, if n = 100, prime 5 has highest power of log5100, i.e. the maximum number of time we can divide 100 by 5, which is 2. You can see, 52 = 25 which is less than 100, and 53 = 125 which is greater than 100. So here, for n = 100, p = 5 we get a = 2.
So, for each prime pi until n, we calculate ai , which is the maximum power of that prme till n. Then, lcm = p1a1 . p2a2 . ... . pkak , which is the required answer.
Problem with Naive Solution
n can be equal to 108, and T can be equal to 104 . There are roughly k = 6x106 primes till 108 . So for each case, if we intend to use the naive solution, the complexity for each case would be O(k log n), where k is the number of prime factors till n. So, total complexity will be O(Tk log n) , which won't pass the time limit of 4s.
Observations
- Every prime not greater than n appears in the LCM at least once, i.e. ai is at least 1.
Why: Since pi <= n, when we calculate lcm(1, 2, 3, ... , n), pi is included in 1, 2, 3, ..., n . So it appears atleast once in the LCM.
- No prime greater than n will appear in the LCM.
Why: Let pi > n is a prime. There is no multiple of pi that is included in 1, 2, 3, ... , n since the lowest multiple of pi is pi itself, which is greater than n.
- Every prime till n and not greater than sqrt(n) will appear atleast twice in the LCM.
Why: Let pi <= sqrt(n) be a prime not greater than n. Let piai appear in the prime. Since pi <= sqrt(n), pi2 <= n, so ai is at least 2.
- Every prime till n and greater than sqrt(n) will apear exactly once in the LCM.
Why: Let pi > sqrt(n) be a prime not greater than n. Let piai appear in the prime. Now, pi > sqrt(n) implies that pi2 > n, so ai can not be greater than 1. And, according to the first observation, ai is at least 1. So, ai is exactly 1, which means pi appears exactly once in the LCM.
These observation paves the way for some workarounds and optimizations that gives a faster approach.
Faster Approach
If we know the product of all primes till n, we can consider this product as "taking each prime till n atleast once" for the LCM. So we can precalculate this before processing any of the test cases, for all possible n using techniques like cumulative sum (here we can call it cumulative product).
For each case, we can find the product of primes till n using a binary search. Taking this product implies we took all the primes till n at least once. So, we don't need to consider primes greater than sqrt(n) according to the fourth observation.
Now we can only consider primes till sqrt(n). If any prime pi appears ai times in n, we multiply pi , ai - 1 times with the answer (since we already took these primes once in the previous step).
Done!
The preprocessing needs O(n) operations to complete, for just once, since we are just running a loop till the max value of n.
For each case, we are considering all primes till sqrt(n) and checking how many times they appear till n. This requires O(sqrt(n) * log2(n)) operations at most.
So, total complexity for T cases = O(n + T * sqrt(n) * log2(n)) , which passes the 4s time limit.
C++ code
#include "bits/stdc++.h"
using namespace std;
using ll = long long;
using uint = unsigned int;
const int mx = 1e8 + 9;
const int mxprm = 6e6 + 9;
int psz = 0;
bitset <mx> mark;
uint primes[mxprm];
uint mul[mxprm];
void sieve() {
mark[0] = mark[1] = 1;
primes[psz++] = 2;
int lim = sqrt(mx * 1.0) + 2;
for (int i = 4; i < mx; i += 2) mark[i] = 1;
for (int i = 3; i < mx; i += 2) {
if (!mark[i]) {
primes[psz++] = i;
if (i <= lim)
for (int j = i * i; j < mx; j += i)
mark[j] = 1;
}
}
}
int main() {
sieve();
mul[0] = 2;
for (int i = 1; i < psz; i++)
mul[i] = (primes[i] * mul[i - 1]);
int tc; scanf("%d", &tc);
int kase = 0;
while (tc--) {
int n; scanf("%d", &n);
printf("Case %d: ", ++kase);
uint ans = 1;
int idx = upper_bound(primes, primes + psz, n) - primes;
idx--;
ans *= mul[idx];
for (int i = 0; i < psz; i++) {
ll p = primes[i];
ll x = n;
ll a = 0;
if (p * p > n) break;
while (x >= p) {
x /= p;
a++;
}
ans *= pow(p, a - 1);
}
printf("%lld\n", ans);
}
}
Tutorial source