#![allow(dead_code)]
use crate::prime_number::prime_factors;
use crate::scanner::Scanner;
fn main() {
    let mut scan = Scanner::new();
    let n = scan.read::<i64>();
    let f = prime_factors(n);
    let mut cnt = 0i64;
    for &(mut value) in f.values() {
        let mut k = 1;
        while value >= k {
            value -= k;
            k += 1;
            cnt += 1;
        }
    }
    println!("{}", cnt);
}
mod prime_number {
    use crate::counter::Counter;
    pub fn is_prime(n: i64) -> bool {
        for i in (2..).take_while(|i| i * i <= n) {
            if n % i == 0 {
                return false;
            }
        }
        n != 1
    }
    pub fn divisors(n: i64) -> Vec<i64> {
        let mut ret = vec![];
        for i in (1..).take_while(|i| i * i <= n) {
            if n % i == 0 {
                ret.push(i);
                if i != n / i {
                    ret.push(n / i);
                }
            }
        }
        ret
    }
    pub fn prime_factors(n_: i64) -> Counter<i64> {
        let mut ret = Counter::new();
        let n = std::cell::Cell::new(n_);
        for i in (2..).take_while(|&i| i * i <= n.get()) {
            while n.get() % i == 0 {
                ret[i] += 1;
                n.set(n.get() / i);
            }
        }
        if n.get() != 1 {
            ret[n.get()] = 1;
        }
        ret
    }
    pub fn sieve(n: usize) -> (Vec<usize>, Vec<bool>) {
        let mut primes = vec![];
        let mut is_prime = vec![true; n + 1];
        is_prime[0] = false;
        is_prime[1] = false;
        for i in 2..n + 1 {
            if is_prime[i] {
                primes.push(i);
                for j in (2..).map(|j| j * i).take_while(|&j| j <= n) {
                    is_prime[j] = false;
                }
            }
        }
        (primes, is_prime)
    }
}
mod counter {
    use std::collections::HashMap;
    use std::hash::Hash;
    use std::ops::*;
    #[derive(Clone, Debug)]
    pub struct Counter<K: Eq + Hash> {
        pub cnt: HashMap<K, i64>,
        pub d: i64,
    }
    impl<K: Eq + Hash> Counter<K> {
        pub fn new() -> Counter<K> {
            Counter {
                cnt: HashMap::new(),
                d: 0,
            }
        }
        #[doc = " Remove key when the value <= 0"]
        pub fn dec(&mut self, key: K, delta: i64) {
            if self.by_ref(&key) - delta <= 0 {
                self.remove(&key);
            } else {
                *self.cnt.get_mut(&key).unwrap() -= delta;
            }
        }
        pub fn by_ref(&self, key: &K) -> i64 {
            *self.cnt.get(key).unwrap_or(&self.d)
        }
    }
    impl<K: Eq + Hash> Deref for Counter<K> {
        type Target = HashMap<K, i64>;
        fn deref(&self) -> &Self::Target {
            &self.cnt
        }
    }
    impl<K: Eq + Hash> DerefMut for Counter<K> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            &mut self.cnt
        }
    }
    impl<K: Eq + Hash> Index<K> for Counter<K> {
        type Output = i64;
        fn index(&self, index: K) -> &Self::Output {
            self.cnt.get(&index).unwrap_or(&self.d)
        }
    }
    impl<K: Eq + Hash + Clone> IndexMut<K> for Counter<K> {
        fn index_mut(&mut self, index: K) -> &mut i64 {
            if !self.cnt.contains_key(&index) {
                self.cnt.insert(index.clone(), self.d);
            }
            self.cnt.get_mut(&index).unwrap()
        }
    }
    impl<K: Eq + Hash> std::iter::FromIterator<K> for Counter<K> {
        fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
            let mut cnt = HashMap::new();
            for i in iter {
                *cnt.entry(i).or_insert(0) += 1;
            }
            Counter { cnt, d: 0 }
        }
    }
    impl<T: Eq + Hash + Clone> Add for Counter<T> {
        type Output = Counter<T>;
        fn add(self, other: Counter<T>) -> Counter<T> {
            let mut ret = Counter::new();
            for (k, v) in self.iter().chain(other.iter()) {
                ret[k.clone()] += *v;
            }
            ret
        }
    }
    impl<T: Eq + Hash + Clone> AddAssign for Counter<T> {
        fn add_assign(&mut self, other: Self) {
            *self = self.clone().add(other);
        }
    }
}
mod scanner {
    use std::io::{stdin, BufReader, Bytes, Read, Stdin};
    use std::str::FromStr;
    pub struct Scanner {
        buf: Bytes<BufReader<Stdin>>,
    }
    impl Scanner {
        pub fn new() -> Scanner {
            Scanner {
                buf: BufReader::new(stdin()).bytes(),
            }
        }
        pub fn read_next<T: FromStr>(&mut self) -> Option<T> {
            let token = self
                .buf
                .by_ref()
                .map(|c| c.unwrap() as char)
                .skip_while(|c| c.is_whitespace())
                .take_while(|c| !c.is_whitespace())
                .collect::<String>();
            token.parse::<T>().ok()
        }
        pub fn read<T: FromStr>(&mut self) -> T {
            self.read_next().unwrap()
        }
        pub fn readn<T: FromStr>(&mut self, n: usize) -> Vec<T> {
            (0..n).map(|_| self.read::<T>()).collect()
        }
        pub fn chars(&mut self) -> Vec<char> {
            self.read::<String>().chars().collect()
        }
    }
}



Measured memory_bytes: 2196