Problem:
Score : 400 points

Problem Statement

Given is a positive integer N. Consider repeatedly applying the operation below on N:

First, choose a positive integer z satisfying all of the conditions below:

z can be represented as z=p^e, where p is a prime number and e is a positive integer;

z divides N;

z is different from all integers chosen in previous operations.

Then, replace N with N/z.

Find the maximum number of times the operation can be applied.

Constraints

All values in input are integers.

1 \leq N \leq 10^{12}

Input

Input is given from Standard Input in the following format:

N

Output

Print the maximum number of times the operation can be applied.

Sample Input 1

24

Sample Output 1

3

We can apply the operation three times by, for example, making the following choices:

Choose z=2 (=2^1). (Now we have N=12.)

Choose z=3 (=3^1). (Now we have N=4.)

Choose z=4 (=2^2). (Now we have N=1.)

Sample Input 2

1

Sample Output 2

0

We cannot apply the operation at all.

Sample Input 3

64

Sample Output 3

3

We can apply the operation three times by, for example, making the following choices:

Choose z=2 (=2^1). (Now we have N=32.)

Choose z=4 (=2^2). (Now we have N=8.)

Choose z=8 (=2^3). (Now we have N=1.)

Sample Input 4

1000000007

Sample Output 4

1

We can apply the operation once by, for example, making the following choice:

z=1000000007 (=1000000007^1). (Now we have N=1.)

Sample Input 5

997764507000

Sample Output 5

7

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
class Program
{
    static void Main(string[] args)
    {
            // 要は引数の因数分解して累乗で表示出来る違う約数(一度使った素因数は抜かしていく)の数
            // 前準備
            // 1兆の値の約数を求めるのに1から1兆まで除算して余りが0になる値を計算したらどう見てもTimeOver必死
            // 1兆の値の約数を求めるのには因数分解が必要
            // 1兆の値の因数分解する為には2 から 999983 までの素数(√1兆 = 1000000なので)が判明していれば素因数分解が出来る
            // 2から999983まので素数取得
            // 2から999983まので素数取得する為には2から997までの素数(√999983 = 999.9915なので)が判明していればOK
            // 順次997までの素数は97までの素数が判明していればよい97までの素数は  2, 3, 5, 7だけでよい。
            List<int> lstPrm = new List<int>() { 2, 3, 5, 7, };
            int iRng = 97;                                          // 調査範囲 97
            GetPrim(ref lstPrm, iRng);
            iRng = 997;                                             // 調査範囲 997
            GetPrim(ref lstPrm, iRng);
            iRng = 999983;                                          // 調査範囲 999983まで
            GetPrim(ref lstPrm, iRng);

            // 入力
            string[] sS = System.Console.ReadLine().Trim().Split(' ');
            long iInpN = long.Parse(sS[0]);
            //// Debug入力
            //List<ClsCal> lstCal = new List<ClsCal>();
            //for (long i = 151; i < 1002; i++) { lstCal.Add(new ClsCal() { iN = i }); }

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            // 解析
            List<long> lstFctN0 = new List<long>();                         // N  の素因数分解要素
            GetFact(lstFctN0, lstPrm, iInpN);                               // N  の素因数分解実行
            List<ClsPrm> lstYak = new List<ClsPrm>();                       // 素因数を同じ値の数で管理(List)
            GetYaku3(lstYak, lstFctN0);                                     // N  の素因数分解の素数の数で管理
            int iOut = 0;                                                   // 素因数の累乗で表せられる数
            for (int i = 0; i < lstYak.Count; i++)
            {
                int iEx = 1;                                                // 素因数の累乗数
                while (true)
                {
                    if (iEx > lstYak[i].iPow) { break; }
                    iOut += 1;
                    lstYak[i].iPow -= iEx;                                  // 一度使った累乗数は抜かす
                    iEx += 1;
                }
            }

            // 出力
            System.Console.WriteLine("{0}", iOut.ToString().Trim());

            sw.Stop();
#if DEBUG
            Console.WriteLine();
            Console.WriteLine("{0}", sw.Elapsed.ToString());
            Console.WriteLine("続行するには何かキーを押してください．．．");
            Console.ReadKey();
#endif
    }

        // 素数算出
        private static void GetPrim(ref List<int> lstPrm, int iRng)
        {
            // 調査対象定義
            List<Boolean> lstTag = new List<Boolean>();                                         // 配列(true:素数 false:合成)
            for (int i = 0; i <= iRng / 2; i += 1) { lstTag.Add(false); lstTag.Add(true); }     // 予め 2の倍数は false にしておく

            // 調査
            lstTag[1] = false;                                                                  // 1は除外
            lstTag[2] = true;                                                                   // 2は素数
            for (int i = 1; i < lstPrm.Count; i++)                                              // 調査素数 2は除外しているので3から
            {
                int iMtS = 2;                                                                   // 例 3では100000000
                int iMtE = iRng / lstPrm[i];                                                    // 例 3では133333333
                for (int j = iMtS; j <= iMtE; j++)
                {
                    int iTmp = (int)(lstPrm[i] * j);
                    if (iTmp >= 0 && iTmp <= iRng) { lstTag[iTmp] = false; }
                }
            }
            // 結果
            List<int> lstNew = new List<int>();
            for (int i = 0; i < lstTag.Count; i++) { if (lstTag[i]) { lstNew.Add(i); } }
            lstPrm = lstNew;
        }

        // 素因数分解
        private static void GetFact(List<long> lstFct, List<int> lstPrm, long iN)
        {
            for (int i = 0; i < lstPrm.Count; i++)
            {
                if (iN < lstPrm[i]) { break; }
                while (true)
                {
                    if (iN % lstPrm[i] == 0)
                    {
                        lstFct.Add(lstPrm[i]);
                        iN = iN / lstPrm[i];
                    }
                    else { break; }
                }
            }
            if (iN != 0 && iN != 1) { lstFct.Add(iN); }                         // 最後の素数
        }

        // 素因数分解要素内をDistinctにする
        private static void DitFact(List<long> lstFct)
        {
            List<string> lstTmp = new List<string>();
            for (int i = 0; i < lstFct.Count; i++)
            {
                lstTmp.Add(lstFct[i].ToString());
            }
            lstTmp = lstTmp.Distinct().ToList();
            lstFct.Clear();
            for (int i = 0; i < lstTmp.Count; i++) { lstFct.Add(long.Parse(lstTmp[i])); }
        }

        // 素因数分解から(素因数の各数(累乗値)取得
        private static void GetYaku3(List<ClsPrm> lstPrm, List<long> lstFct)
        {
            if (lstFct.Count == 0) { return; }                                                  // 素因数がなければ抜ける
            List<string> lstDeb = new List<string>();
            Dictionary<long, int> dicPrm = new Dictionary<long, int>();                         // 素因数を同じ値の数で管理(Dictionary)
            for (int i = 0; i < lstFct.Count; i++)                                             //   A^x B^y C^z のように Aがx個 Bがy個 cがy個 で管理
            {
                if (dicPrm.ContainsKey(lstFct[i])) { dicPrm[lstFct[i]] += 1; }                  // 同じ値は+1個
                else { dicPrm.Add(lstFct[i], 1); }                                              // 初めての値は1個
            }
            foreach (KeyValuePair<long, int> kvp in dicPrm)
            {
                lstPrm.Add(new ClsPrm() { iPrm = kvp.Key, iPow = kvp.Value, iPed = 0 });
            }
        }

        // 素因数分解から約数取得その２(素因数の各累乗値から Nが大きくても短縮) ← 次回からは約数取得はこのロジックでOK 本採用
        private static void GetYaku2(List<long> lstYak, List<long> lstFct)
        {
            if (lstFct.Count == 0) { return; }                                                  // 素因数がなければ抜ける
            List<string> lstDeb = new List<string>();
            Dictionary<long, int> dicPrm = new Dictionary<long, int>();                         // 素因数を同じ値の数で管理(Dictionary)
            for (int i = 0; i < lstFct.Count; i++)                                             //   A^x B^y C^z のように Aがx個 Bがy個 cがy個 で管理
            {
                if (dicPrm.ContainsKey(lstFct[i])) { dicPrm[lstFct[i]] += 1; }                  // 同じ値は+1個
                else { dicPrm.Add(lstFct[i], 1); }                                              // 初めての値は1個
            }
            List<ClsPrm> lstPrm = new List<ClsPrm>();                                           // 素因数を同じ値の数で管理(List)
            foreach (KeyValuePair<long, int> kvp in dicPrm)
            {
                lstPrm.Add(new ClsPrm() { iPrm = kvp.Key, iPow = kvp.Value, iPed = 0 });
            }
            int iPowMax = 1;                                                                    // 約数の数
            foreach (KeyValuePair<long, int> kvp in dicPrm) { iPowMax *= (kvp.Value + 1); }     // 約数の数=各素因数の累乗数+1の和
            for (int i = 0; i < iPowMax; i++)                                                   // 約数の数だけ回して約数取得計算
            {
                lstPrm[0].iPed += 1;
                for (int j = 0; j < lstPrm.Count; j++)                                          // 今回の各素因数の累乗値取得
                {
                    if (lstPrm[j].iPed > lstPrm[j].iPow)                                        // この素因数の累乗可能数より大の場合
                    {
                        lstPrm[j].iPed = 0;                                                     // この素因数の累乗は0クリアで
                        if (j + 1 < lstPrm.Count) { lstPrm[j + 1].iPed += 1; }                  // 次の素因数の累乗値を+1(次があれば)
                    }
                }
                long iRet = 1;
                for (int j = 0; j < lstPrm.Count; j++)                                          // 今回の各素因数の累乗値で約数計算
                {
                    for (int k = 0; k < lstPrm[j].iPed; k++)                                    // 今回の各素因数の累乗値だけ掛け回す 0 なら掛けない
                    {
                        iRet *= lstPrm[j].iPrm;                                                 // Math.Pow()でもいいけど doble long 型変換でオーバーフローは嫌なので
                    }
                }
                lstYak.Add(iRet);
            }
            lstYak.Sort();
        }

        // 素因数分解から約数取得(素因数の組み合わせから)
        private static void GetYaku(List<long> lstYak, List<long> lstFct)
        {
            List<string> lstDeb = new List<string>();
            int iCnt = lstFct.Count;                                                        // 素因数数
            int[] nums = new int[iCnt];
            for (int i = 0; i < iCnt; i++) { nums[i] = i; }
            for (int i = iCnt; i > 0; i--)                                                  // 取得する数 iCnt個から少なくしていく
            {
                var combinations = Combination.Enumerate(nums, i, withRepetition: false);   // 配列内の考えられる組み合わせ
                foreach (var elem in combinations)
                {
                    long iL = 1;
                    for (int j = 0; j < elem.Length; j++) { iL *= lstFct[elem[j]]; }        // ある組み合わせの積
                    lstDeb.Add(iL.ToString());                                              // それがある約数
                }
            }
            // 重複排除
            lstDeb.Sort();
            lstDeb = lstDeb.Distinct().ToList();
            for (int i = 0; i < lstDeb.Count; i++) { lstYak.Add(long.Parse(lstDeb[i])); }
            lstYak.Sort();
        }

        // N個からK個取得する組み合わせ(但しNが大きいと時間が掛かる)
        private static class Combination
        {
            public static IEnumerable<T[]> Enumerate<T>(IEnumerable<T> items, int k, bool withRepetition)
            {
                if (k == 1)
                {
                    foreach (var item in items)
                        yield return new T[] { item };
                    yield break;
                }
                foreach (var item in items)
                {
                    var leftside = new T[] { item };

                    // item よりも前のものを除く （順列と組み合わせの違い)
                    // 重複を許さないので、unusedから item そのものも取り除く
                    var unused = withRepetition ? items : items.SkipWhile(e => !e.Equals(item)).Skip(1).ToList();

                    foreach (var rightside in Enumerate(unused, k - 1, withRepetition))
                    {
                        yield return leftside.Concat(rightside).ToArray();
                    }
                }
            }
        }

        private class ClsPrm
        {
            public long iPrm  { get; set; } = 0L;                       // 素数
            public int  iPow  { get; set; } = 0;                        // 累乗
            public int  iPed  { get; set; } = 0;                        // 累乗カウント値
            public override bool Equals(object obj)                     // 重複排除用定義 .Distinct()用
            {
                var p = obj as ClsPrm;
                if (p == null) { return false; }
                return (this.iPrm == p.iPrm);
            }
            public override int GetHashCode()                           // 重複排除用定義 .Distinct()用
            => iPrm.GetHashCode();                                      // 重複排除用定義 .Distinct()用
        }

        private class ClsCal
        {
            public long iN { get; set; } = 0L;                    // N
            public List<long> iK { get; set; } = new List<long>();      // K
        }

}