F - GCD or MIN Editorial by lucifer1004
An important observation is \(\gcd(x,y)\leq\min(x,y)\). So the minimum of all numbers \(\min{A_i}\) proves to be the maximum answer we could get.
Since all the possible answers must be a factor of some number in the array, we can find the factors of every number within \(\mathcal{O}(N\sqrt{\max{A_i}})\) iterations.
Not all factors can be the final answer. Supposing that a factor \(k<\min{A_i}\) can be the final answer,
- \(k\) must be a factor of some number \(A_i\).
- \(k\) comes from a series \(\gcd\) operations, e.g., \(\gcd(A_{p_1},A_{p_2},\dots)\). \(k\) cannot be \(A_i\) itself since we have \(k<\min{A_i}\).
- Since the order of \(\gcd\) operations does not matter, the first time we meet \(k\), supposing that we are dealing with the number \(A_i\), we can set \(memo[k]\) to \(A_i\). Note that here we have \(memo[k]\geq k\).
- Our goal is to get \(k\) in the end (\(memo[k]=k\)), so each time we have the chance to perform a \(\gcd\) operation (meaning that \(k\) is also a factor of some other number \(A_j\)), we should perform the \(\gcd\) operation to reduce the number \(memo[k]\) to \(\gcd(memo[k],A_j)\). Since both \(memo[k]\) and \(A_j\) can be divided by \(k\), it is assured that \(memo[k]\) can be divided by \(k\). We do the \(\gcd\) operation whenever possible because \(\gcd(x,y)\leq\min(x,y)\), and we want to reduce the value \(memo[k]\).
After processing all the numbers, we check our \(memo\) and count for how many factors \(memo[k]=k\) holds.
And the final answer would be that count plus \(1\) (by using \(\min\) operations only).
- Time complexity is \(\mathcal{O}(N\sqrt{\max{A_i}}\log{\max{A_i}})\).
- Space complexity is \(\mathcal{O}(Nd)\), where \(d\) is the maximum number of factors of a single number.
Code (C++)
#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i)
cin >> a[i];
int lo = *min_element(a.begin(), a.end());
unordered_map<int, int> memo;
for (int ai : a) {
for (int j = 1; j * j <= ai && j < lo; ++j) {
if (ai % j == 0) {
memo[j] = __gcd(ai, memo[j]);
if (ai / j < lo)
memo[ai / j] = __gcd(ai, memo[ai / j]);
}
}
}
int ans = 1;
for (auto [factor, terminal] : memo)
if (factor == terminal)
ans++;
cout << ans;
}
Code (Rust)
use proconio::input;
use std::collections::HashMap;
fn gcd(x: usize, y: usize) -> usize {
if y == 0 {
x
} else {
gcd(y, x % y)
}
}
fn main() {
input! {
n: usize,
a: [usize; n],
}
let mut factors: HashMap<usize, usize> = HashMap::new();
let lo = *a.iter().min().unwrap();
for i in 0..n {
for j in 1..lo {
if j * j > a[i] {
break;
}
if a[i] % j == 0 {
let original = *factors.entry(j).or_insert(a[i]);
factors.insert(j, gcd(original, a[i]));
if a[i] / j < lo {
let original = *factors.entry(a[i] / j).or_insert(a[i]);
factors.insert(a[i] / j, gcd(original, a[i]));
}
}
}
}
let mut ans = 1;
for (original, terminal) in factors {
if original == terminal {
ans += 1;
}
}
println!("{}", ans);
}
posted:
last update: