Problem:
Score : 500 points

Problem Statement

Given are N positive integers A_1,...,A_N.

Consider positive integers B_1, ..., B_N that satisfy the following condition.

Condition: For any i, j such that 1 \leq i < j \leq N, A_i B_i = A_j B_j holds.

Find the minimum possible value of B_1 + ... + B_N for such B_1,...,B_N.

Since the answer can be enormous, print the sum modulo (10^9 +7).

Constraints

1 \leq N \leq 10^4

1 \leq A_i \leq 10^6

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
A_1 ... A_N

Output

Print the minimum possible value of B_1 + ... + B_N for B_1,...,B_N that satisfy the condition, modulo (10^9 +7).

Sample Input 1

3
2 3 4

Sample Output 1

13

Let B_1=6, B_2=4, and B_3=3, and the condition will be satisfied.

Sample Input 2

5
12 12 12 12 12

Sample Output 2

5

We can let all B_i be 1.

Sample Input 3

3
1000000 999999 999998

Sample Output 3

996989508

Print the sum modulo (10^9+7).

Solution in C (GCC 5.4.1) (judge verdict: Wrong Answer)

#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// 内部定数
#define D_MOD			1000000007								// 除数(10の9乗+7)
#define D_ARRAY_MAX		10000									// 最大配列数
#define D_PRM_MAX		10										// 最大素数数
#define D_VAL_MAX		1000000									// 最大値
#define D_SEGT_CNT		16384									// セグメントツリーデータ数(2の14乗)

// 内部構造体 - 素数情報
typedef struct Prm {
	int miVal;													// 素数
	int miCnt;													// 素数数
} Prm;

// 内部構造体 - 値情報
typedef struct Val {
	Prm mz1Prm[D_PRM_MAX];										// 素数
	int miPCnt;													// 素数数
} Val;

// 内部変数
static FILE *szpFpI;											// 入力
static Val sz1Array[D_ARRAY_MAX];								// 配列
static int siACnt;												// 配列数
static int si1Lcm[D_VAL_MAX];									// 最小公倍数
static Prm sz1Lcm[D_ARRAY_MAX];									// 最小公倍数(圧縮)
static int siLCnt;												// 最小公倍数(圧縮)数
static long long sl1SegT[D_SEGT_CNT * 2];						// セグメントツリー
static int siSCNo;												// セグメントツリー - 子の開始位置

// 内部変数 - テスト用
#ifdef D_TEST
	static int siRes;
	static FILE *szpFpA;
	static int siTNo;
#endif

// １行出力
int
fOutLine(
	char *pcpLine				// <I> １行
)
{
	char lc1Buf[1024];

#ifdef D_TEST
	lc1Buf[0] = '\0';
	fgets(lc1Buf, sizeof(lc1Buf), szpFpA);
	if (strcmp(lc1Buf, pcpLine)) {
		siRes = -1;
	}
#else
	printf("%s", pcpLine);
#endif

	return 0;
}

// 素数 - 追加
int
fAddPrm(
	int piPrm					// <I> 素数
	, int piCnt					// <I> 素数数
	, Val *pzpVal				// <O> セット先
)
{
	// セット
	Prm *lzpPrm = &pzpVal->mz1Prm[pzpVal->miPCnt];
	lzpPrm->miVal = piPrm;
	lzpPrm->miCnt = piCnt;
	pzpVal->miPCnt++;

	return 0;
}

// 素因数分解
int
fDivPrm(
	int piVal					// <I> 対象値
	, Val *pzpVal				// <O> セット先
)
{
	int i;

	// 上限
	int liLimit = (int)sqrt((double)piVal);

	// 素因数分解
	for (i = 2; i <= liLimit; i++) {

		// 分解終了
		if (piVal == 1) {
			break;
		}

		// 分解
		int liCnt = 0;
		while (piVal % i == 0) {
			piVal /= i;
			liCnt++;
		}

		// 分解あり
		if (liCnt > 0) {
			fAddPrm(i, liCnt, pzpVal);
		}
	}

	// 上限を超える素数
	if (piVal != 1) {
		fAddPrm(piVal, 1, pzpVal);
	}

	return 0;
}

// 検索 - 比較
int
fBSrhCmp(
	int piVal					// <I> 値
	, Prm *pzpArray				// <I> 配列
	, int piANo					// <I> 配列番号 0～
)
{
	if (piVal < pzpArray[piANo].miVal) {
		return -1;
	}
	else if (piVal > pzpArray[piANo].miVal) {
		return 1;
	}

	return 0;
}

// 検索
// 戻り値：[>=0]配列番号 [-1]なし
int
fBSrh(
	int piVal					// <I> 値
	, Prm *pzpArray				// <I> 配列
	, int piACnt				// <I> 配列数
)
{
	// 初期範囲
	int liSNo = 0;
	int liENo = piACnt - 1;

	// 検索
	while (1) {

		// 中間位置
		int liMNo = (liSNo + liENo) / 2;

		// 比較
		int liRet = fBSrhCmp(piVal, pzpArray, liMNo);

		// 一致チェック
		if (liRet == 0) {
			return liMNo;
		}

		// 範囲を絞る
		if (liRet < 0) {							// 左側へ
			liENo = liMNo - 1;
		}
		else {										// 右側へ
			liSNo = liMNo + 1;
		}

		// 範囲チェック
		if (liSNo > liENo) {
			return -1;
		}
	}

	return -1;
}

// セグメントツリー - 子の開始位置 - セット
int
fSegTSetCSNo(
	int piCnt					// <I> 子データ数
)
{
	siSCNo = 1;
	while (siSCNo < piCnt) {
		siSCNo *= 2;
	}

	return 0;
}

// セグメントツリー - ２つの値の処理 - 積
long long
fSegTVal2(
	long long plVal1			// <I> 値１
	, long long plVal2			// <I> 値２
)
{
	return plVal1 * plVal2 % D_MOD;
}

// セグメントツリー - 親データセット
int
fSegTSetPData(
)
{
	int i;

	// 最初の親番号範囲
	int liPNo1 = siSCNo / 2;
	int liPNo2 = siSCNo - 1;

	// 作成
	while (liPNo1 > 0) {
		for (i = liPNo1; i <= liPNo2; i++) {

			// セット
			sl1SegT[i] = fSegTVal2(sl1SegT[i * 2], sl1SegT[i * 2 + 1]);
		}

		// 次の親番号範囲
		liPNo2 = liPNo1 - 1;
		liPNo1 /= 2;
	}

	return 0;
}

// セグメントツリー - 取得
long long
fSegTGetRngMain(
	int piGetS					// <I> 取得範囲 - 開始 0～
	, int piGetE				// <I> 取得範囲 - 終了 0～
	, int piNNo					// <I> 現在位置 1～
	, int piNowS				// <I> 現在範囲 - 開始 0～
	, int piNowE				// <I> 現在範囲 - 終了 0～
)
{
	long long llRet, llRet2;

	// 内包チェック
	if (piGetS <= piNowS && piNowE <= piGetE) {
		return sl1SegT[piNNo];
	}

	// 中間位置
	int liCenter = (piNowS + piNowE) / 2;

	// 初期値
	llRet = 1;

	// 左側
	if (piGetS <= liCenter) {
		llRet2 = fSegTGetRngMain(piGetS, piGetE, piNNo * 2, piNowS, liCenter);
		llRet = fSegTVal2(llRet, llRet2);
	}

	// 右側
	if (piGetE >= liCenter + 1) {
		llRet2 = fSegTGetRngMain(piGetS, piGetE, piNNo * 2 + 1, liCenter + 1, piNowE);
		llRet = fSegTVal2(llRet, llRet2);
	}

	return llRet;
}
long long
fSegTGetRng(
	int piGetS					// <I> 取得範囲 - 開始 0～
	, int piGetE				// <I> 取得範囲 - 終了 0～
)
{
	return fSegTGetRngMain(piGetS, piGetE, 1, 0, siSCNo - 1);
}

// 実行メイン
int
fMain(
)
{
	int i, j, k;
	char lc1Buf[1024];

	// 配列数 - 取得
	fgets(lc1Buf, sizeof(lc1Buf), szpFpI);
	sscanf(lc1Buf, "%d", &siACnt);

	// 配列 - 取得
	for (i = 0; i < siACnt; i++) {
		int liVal;
		fscanf(szpFpI, "%d", &liVal);

		// 素因数分解
		fDivPrm(liVal, &sz1Array[i]);
	}
	fgets(lc1Buf, sizeof(lc1Buf), szpFpI);

	// 最小公倍数 - セット
	for (i = 0; i < siACnt; i++) {
		for (j = 0; j < sz1Array[i].miPCnt; j++) {
			Prm *lzpPrm = &sz1Array[i].mz1Prm[j];
			if (si1Lcm[lzpPrm->miVal] < lzpPrm->miCnt) {
				si1Lcm[lzpPrm->miVal] = lzpPrm->miCnt;
			}
		}
	}

	// 最小公倍数 - 圧縮
	siLCnt = 0;
	for (i = 0; i < D_VAL_MAX; i++) {
		if (si1Lcm[i] > 0) {
			sz1Lcm[siLCnt].miVal = i;
			sz1Lcm[siLCnt].miCnt = si1Lcm[i];
			siLCnt++;
		}
	}

	// セグメントツリー - 初期化
	for (i = 0; i < D_SEGT_CNT; i++) {
		sl1SegT[i] = 1;
	}

	// セグメントツリー - 子の開始位置 - セット
	fSegTSetCSNo(siLCnt);

	// セグメントツリー - 子データセット
	for (i = 0; i < siLCnt; i++) {
		for (j = 0; j < sz1Lcm[i].miCnt; j++) {
			sl1SegT[siSCNo + i] = sl1SegT[siSCNo + i] * (long long)sz1Lcm[i].miVal % D_MOD;
		}
	}

	// セグメントツリー - 親データセット
	fSegTSetPData();

	// 合計 - 取得
	long long llSum = 0;
	for (i = 0; i < siACnt; i++) {

		// BN - 取得
		long long llMulti = 1;
		int liSNo = 0;
		for (j = 0; j <= sz1Array[i].miPCnt; j++) {

			// ANの素因数
			int liENo;
			if (j < sz1Array[i].miPCnt) {
				Prm *lzpPrm = &sz1Array[i].mz1Prm[j];
				liENo = fBSrh(lzpPrm->miVal, sz1Lcm, siLCnt);
				int liCnt = sz1Lcm[liENo].miCnt - lzpPrm->miCnt;
				for (k = 0; k < liCnt; k++) {
					llMulti = llMulti * (long long)lzpPrm->miVal % D_MOD;
				}
				liENo--;
			}
			else {
				liENo = siLCnt - 1;
			}

			// 最小公倍数の積
			if (liSNo <= liENo) {
				long long llVal = fSegTGetRng(liSNo, liENo);
				llMulti = llMulti * llVal % D_MOD;
			}

			// 次の開始位置
			liSNo = liENo + 2;
		}

		// 合計 - 加算
		llSum = (llSum + llMulti) % D_MOD;
	}

	return (int)llSum;
}

// １回実行
int
fOne(
)
{
	int liRet;
	char lc1Buf[1024];

	// データ - 初期化
	memset(sz1Array, 0, sizeof(sz1Array));						// 配列
	memset(si1Lcm, 0, sizeof(si1Lcm));							// 最小公倍数

	// 入力 - セット
#ifdef D_TEST
	sprintf(lc1Buf, ".\\Test\\T%d.txt", siTNo);
	szpFpI = fopen(lc1Buf, "r");
	sprintf(lc1Buf, ".\\Test\\A%d.txt", siTNo);
	szpFpA = fopen(lc1Buf, "r");
	siRes = 0;
#else
	szpFpI = stdin;
#endif

	// 実行メイン
	liRet = fMain();

	// 出力
	sprintf(lc1Buf, "%d\n", liRet);
	fOutLine(lc1Buf);

	// 残データ有無
#ifdef D_TEST
	lc1Buf[0] = '\0';
	fgets(lc1Buf, sizeof(lc1Buf), szpFpA);
	if (strcmp(lc1Buf, "")) {
		siRes = -1;
	}
#endif

	// テストファイルクローズ
#ifdef D_TEST
	fclose(szpFpI);
	fclose(szpFpA);
#endif

	// テスト結果
#ifdef D_TEST
	if (siRes == 0) {
		printf("OK %d\n", siTNo);
	}
	else {
		printf("NG %d\n", siTNo);
	}
#endif

	return 0;
}

// プログラム開始
int
main()
{

#ifdef D_TEST
	int i;
	for (i = D_TEST_SNO; i <= D_TEST_ENO; i++) {
		siTNo = i;
		fOne();
	}
#else
	fOne();
#endif

	return 0;
}