Problem:
Score : 400 points

Problem Statement

Given are positive integers A and B.

Let us choose some number of positive common divisors of A and B.

Here, any two of the chosen divisors must be coprime.

At most, how many divisors can we choose?

Definition of common divisor

An integer d is said to be a common divisor of integers x and y when d divides both x and y.

Definition of being coprime

Integers x and y are said to be coprime when x and y have no positive common divisors other than 1.

Definition of dividing

An integer x is said to divide another integer y when there exists an integer \alpha such that y = \alpha x.

Constraints

All values in input are integers.

1 \leq A, B \leq 10^{12}

Input

Input is given from Standard Input in the following format:

A B

Output

Print the maximum number of divisors that can be chosen to satisfy the condition.

Sample Input 1

12 18

Sample Output 1

3

12 and 18 have the following positive common divisors: 1, 2, 3, and 6.

1 and 2 are coprime, 2 and 3 are coprime, and 3 and 1 are coprime, so we can choose 1, 2, and 3, which achieve the maximum result.

Sample Input 2

420 660

Sample Output 2

4

Sample Input 3

1 2019

Sample Output 3

1

1 and 2019 have no positive common divisors other than 1.

Solution in C# (Mono 4.6.2.0) (judge verdict: Wrong Answer)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class Program
{
    static void Main(string[] args)
    {
            // 前準備
            // 1兆と1兆の最大公約数は1兆がありえる。その最大公約数の約数が全部公約数になりえる。
            // その為、1兆の素因数分解が必要
            // 1兆までの値の素因数分解なので 2 から 999983 までの素数(√1兆 = 1000000なので)が判明していれば素因数分解が出来る
            // 2から999983まので素数取得
            // 2から999983まので素数取得する為には2から997までの素数(√999983 = 999.9915なので)が判明していればOK
            int iRng = 999983;                                                                   // 調査範囲 999983まで
            List<int> lstPrm = new List<int>() { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 };
            GetPrim(ref lstPrm, iRng);

            // 入力
            string[] sS = System.Console.ReadLine().Trim().Split(' ');
            long iA = long.Parse(sS[0]);
            long iB = long.Parse(sS[1]);

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

            // 解析
            long iE = Gcd(iA, iB);                              // AとBの最大公約数 E
            List<long> lstFct = new List<long>();               // Eの素因数分解要素
            GetFact(lstFct, lstPrm, iE);                        // Eの素因数分解実行
            DitFact(lstFct);                                    // Eの素因数分解のDistinct
            List<long> lstYak = new List<long>();               // Eの約数要素
            GetYaku(lstYak, lstFct);                            // Eの約数要素取得
            List<long> lstTag = new List<long>();               // Eの約数の互いに素要素
            GetTaga(lstTag, lstYak);                            // Eの約数の互いに素要素取得
            lstTag.Insert(0, 1L);                               // 最初に1を追加

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

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

        // DebugDisp
        private static void DebugDisp(long iE, List<long> lstFct, List<long> lstYak, List<long> lstTag)
        {
            string sOut = "";
            System.Console.WriteLine("最大公約数E({0}ｺ)   {1}", 1.ToString("0000").Trim(), iE.ToString().Trim());
            sOut = "";
            for (int i = 0; i < lstFct.Count; i++)
            {
                sOut += lstFct[i].ToString() + " ";
            }
            System.Console.WriteLine("Eの素因数分解({0}ｺ) {1}", lstFct.Count.ToString("0000").Trim(), sOut.ToString().Trim());
            sOut = "";
            for (int i = 0; i < lstYak.Count; i++)
            {
                sOut += lstYak[i].ToString() + " ";
            }
            System.Console.WriteLine("Eの約数({0}ｺ)       {1}", lstYak.Count.ToString("0000").Trim(), sOut.ToString().Trim());
            sOut = "";
            for (int i = 0; i < lstTag.Count; i++)
            {
                sOut += lstTag[i].ToString() + " ";
            }
            System.Console.WriteLine("互いに素なの({0}ｺ)  {1}", lstTag.Count.ToString("0000").Trim(), sOut.ToString().Trim());
        }


        // 最小公倍数取得
        private static long Lcm(long a, long b)
        {
            return a * b / Gcd(a, b);
        }

        // 最大公約数取得
        private static long Gcd(long a, long b)
        {
            if (a < b) { return Gcd(b, a); }
            while (b != 0)
            {
                var remainder = a % b;
                a = b;
                b = remainder;
            }
            return a;
        }

        // 素数算出
        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; }
                }
            }
        }

        // 素因数分解要素内を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 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 static void GetTaga(List<long> lstTag,List<long> lstYak)
        {
            if (lstYak.Count == 0) { return; }                              // 約数が存在しない場合(AとBが互いに素)

            List<ClsGun> lstGun = new List<ClsGun>();
            for (int k = 0; k < lstYak.Count; k++)
            {
                ClsGun oneUsr = new ClsGun();
                oneUsr.lstTag.Add(lstYak[k]);
                lstGun.Add(oneUsr);
                int i = k + 1;
                while (true)
                {
                    if (i == lstYak.Count) { i = 0; }
                    if (i == k) { break; }
                    Boolean bTSo = true;
                    for (int j = 0; j < lstGun[k].lstTag.Count; j++)
                    {
                        long iE = Gcd(lstYak[i], lstGun[k].lstTag[j]);          // ある約数と群内の値の最大公約数
                        if (iE != 1) { bTSo = false; break; }                   // 最大公約数が1じゃない時 互いに素でない
                    }
                    if (bTSo) { lstGun[k].lstTag.Add(lstYak[i]); }              // 互いに素であれば群に追加
                    i += 1;
                }
                lstGun[k].iC = lstGun[k].lstTag.Count;
            }
            lstGun.Sort(delegate (ClsGun mca1, ClsGun mca2) { return mca2.iC - mca1.iC; });     // 降順
            lstTag.AddRange(lstGun[0].lstTag);
        }

        // 互いに素群
        private class ClsGun
        {
            public List<long> lstTag { get; set; } = new List<long>();      // ある群
            public int iC { get; set; } = 0;                                // 要素数
        }

}