Problem:
Score : 400 points

Problem Statement

You are given positive integers N and M.

How many sequences a of length N consisting of positive integers satisfy a_1 \times a_2 \times ... \times a_N = M? Find the count modulo 10^9+7.

Here, two sequences a' and a'' are considered different when there exists some i such that a_i' \neq a_i''.

Constraints

All values in input are integers.

1 \leq N \leq 10^5

1 \leq M \leq 10^9

Input

Input is given from Standard Input in the following format:

N M

Output

Print the number of the sequences consisting of positive integers that satisfy the condition, modulo 10^9 + 7.

Sample Input 1

2 6

Sample Output 1

4

Four sequences satisfy the condition: \{a_1, a_2\} = \{1, 6\}, \{2, 3\}, \{3, 2\} and \{6, 1\}.

Sample Input 2

3 12

Sample Output 2

18

Sample Input 3

100000 1000000000

Sample Output 3

957870001

Solution in C# (Mono-mcs 6.8.0.105) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading.Tasks;


class Program
{
	//素因数分解を行う関数
	/*
	所謂「試し割り法」を使う。配列（素因数とその指数）のリストを返す。
	(e.g.
	例えば、60を素因数分解した場合、（2^2 * 3^1 * 5^1）
	primeFactors[0][0]には、一つ目の素因数2が、
	primeFactors[0][1]には、一つ目の素因数の指数2が、それぞれ格納される)
	*/
	//タイプはulongとする
	static List<ulong[]> TrialDivision(ulong n)
	{
		List<ulong[]> primeFactors = new List<ulong[]>();

		//入力値の平方根
		ulong root = (ulong)Math.Sqrt(n);

		//カウンター（当該素因数で割った回数）、割る数、割られる数
		ulong cnt = 0;
		ulong div = 2;
		ulong tmp = n;

		//平方根を超えるまでループ
		while(div <= root){
			//素因数に持つ場合、割ることができる回数だけ割り続ける
			//(cntの値は、当該素数で何回割れるかを示す（p^aのaに該当）)
			if(tmp % div == 0){
				cnt ++;
				tmp /= div;
			}
			//割り切れない場合
			else{
				//cnt > 0の場合、素因数divで可能な限り割った以後である
				//（cnt = 0の場合、divは素因数ではない）
				if(cnt != 0){
					//素因数と、割った回数とを追加
					ulong[] set = new ulong[2]{div, cnt};
					primeFactors.Add(set);
					//割った回数を初期化
					cnt = 0;
				}
				//次へ
				div += 1;
			}
		}

		//割った結果残ったのが1でない場合、当該素数も素因数である
		if(tmp != 1){
			ulong[] set = new ulong[2]{tmp, 1};
			primeFactors.Add(set);
		}

		//出力
		return primeFactors;
	}

	//Main
	static void Main(string[] args)
	{
		//読み込む
		string[] S = Console.ReadLine().Split(' ');
		int N = int.Parse(S[0]);
		int M = int.Parse(S[1]);

		//Mの素因数分解を行う
		List<ulong[]> factLs = TrialDivision((ulong)M);

		//組み合わせを求める準備（インスタンス作成）
		/*
		引数は、（nCkの）nの最大値（int）と、modを使うか否かのフラグ（bool）
		（※21!の時点でlongの限界を越えるので、基本的にboolはtrueで良い。）
		*/
		Combination Com = new Combination(1000000, true);

		//それぞれの素因数につき組み合わせを求め、かけていく
		long mod = 1000000007;
		long ans = 1;
		foreach(ulong[] factSet in factLs){
			//指数の部分
			int exp = (int)factSet[1];

			//組み合わせ
			long combi = Com.GetCombi(exp+N-1, exp);
			ans *= combi;
			ans %= mod;
		}

		//出力
		Console.WriteLine(ans);
	}
}


//組み合わせの数を計算する為のクラス
class Combination
{
	//累積積を保存する配列
	long[] prodArr;
	//選ばれる対象物の数N
	int capa;
	bool modFlag;

	//mod（もし10^9+7以外を使う場合は、ここを書き換えよ）
	static readonly long mod = 1000000007;

	//コンストラクタ
	/*
	引数は、
	capa（選ばれる対象物（nCkのn）の最大数）、modを使用するか否かのフラグ
	*/
	public Combination(int capa, bool modFlag){
		this.prodArr = new long[capa+1];
		this.capa = capa;
		this.modFlag = modFlag;

		//累積積を初期化
		prodArr[0] = 1;
		prodArr[1] = 1;

		for(int i=2; i<capa+1; i++){
			prodArr[i] = prodArr[i-1] * i;
			//割った余りを計算する場合
			if(this.modFlag){
				prodArr[i] %= mod;
			}
		}
	}

	//nCkを返すメソッド（オブジェクトからアクセス）
	public long GetCombi(int n, int k){
		//nが当初のキャパを超える場合、-1を返して終了
		if(n > this.capa){
			return -1;
		}
		//k > nの場合、0を返して終了
		else if(k > n){
			return 0;
		}

		//半分を超える場合
		k = k > n/2 ? n - k : k;

		//分子
		long numer = prodArr[n];
		//分母
		long denom = prodArr[k] * prodArr[n-k];

		//割った余りを計算する場合
		if(this.modFlag){
			numer %= mod;
			denom %= mod;
		}

		//答え
		long res = 0;
		//1.割った余りを計算するのではない場合
		if(!this.modFlag){
			res = numer / denom;
		}
		//2.割った余りを計算する場合
		else{
			res = ModDivise(numer, denom);
		}

		//出力
		return res;
	}

	//単純に組み合わせnCkを計算し、出力するメソッド
	/*
	外部からも参照可能
	割った余りにするか否かは引数で設定
	*/
	public static long CalcCombi(int n, int k, bool modFlag){
		//半分を超える場合の処理
		k = k > n/2 ? n - k : k;

		//分子、分母
		long numer = 1;
		long denom = 1;
		for(int i=0; i<k; i++){
			//分子を計算
			numer *= (n-i);
			//分母を計算
			denom *= (i+1);

			//割った余りを計算する場合
			if(modFlag){
				numer %= mod;
				denom %= mod;
			}
		}

		//答え
		long res = 0;
		//1.割った余りを計算するのではない場合
		if(!modFlag){
			res = numer / denom;
		}
		//2.割った余りを計算する場合
		else{
			res = ModDivise(numer, denom);
		}

		//出力
		return res;
	}

	//与えられた2整数a,bについて、a / bをmodで割った余りを出力
	//aとbを入力するだけで良い。
	/*
	詳細は、テンプレModInverse.csを参照
	*/
	static long ModDivise(long a, long b)
	{
		//逆元を取得
		long inv = ModInverse(b);

		//a / bのmodを求める
		long res = (a % mod) * inv;
		res %= mod;

		//出力
		return res;
	}

	//整数bとmodについて、modに於けるbの逆元xを返す
	/*
	詳細は、テンプレModInverse.csを参照
	*/
	static long ModInverse(long b)
	{
		//出力x
		long x = 1;
		//xとペアになる数値y
		long y = 0;
		//mod
		long m = mod;

		//拡張ユークリッドの互除法
		while(m != 0){
			long tempo = b / m;

			b -= tempo * m;
			//bとmとを入れ替える
			long swap = b;
			b = m;
			m = swap;

			x -= tempo * y;
			//xとyとを入れ替える
			swap = x;
			x = y;
			y = swap;
		}

		x %= mod;

		if(x < 0){
			x += mod;
		}

		return x;
	}

}