module main; // https://kmjp.hatenablog.jp/entry/2015/01/12/0930 より // 組み合わせ import std; // 多次元配列をある値で埋める void fill(A, T)(ref A a, T value) if (isArray!A) { alias E = ElementType!A; static if (isArray!E) { foreach (ref e; a) fill(e, value); } else { a[] = value; } } // C++ の tie auto tie(StoreElements...)(ref StoreElements stores) { alias Elements = staticMap!(Unqual, StoreElements); template toPointer(T) { alias toPointer = T*; } struct Holder { alias StoreElementsPtrs = staticMap!(toPointer, StoreElements); StoreElementsPtrs storePtrs; this(ref StoreElements stores) { foreach(i, _; StoreElements) { storePtrs[i] = &stores[i]; } } void opAssign(Tuple!Elements values) { foreach(i, _; Elements) { *storePtrs[i] = values[i]; } } } return Holder(stores); } struct ModInt(long MOD) { private: long val; public: // コンストラクタ // useMod:MODで剰余を取る処理を行うかどうか this(long x, bool useMod = true) { if (useMod) { val = x % MOD; if (val < 0) val += MOD; } else val = x; } @property long value() const { return val; } @property ModInt value(long x) { val = x % MOD; if (val < 0) val += MOD; return this; } // 代入演算子 ModInt opAssign(T)(T x) if (isIntegral!T) { val = x % MOD; if (val < 0) val += MOD; return this; } // 演算代入演算子 ModInt opOpAssign(string op, T)(T rhs) pure nothrow if (is(T : ModInt) && (op == "+" || op == "-" || op == "*" || op == "/")) { static if (op == "+") { val += rhs.val; while (val >= MOD) val -= MOD; } else static if (op == "-") { val -= rhs.val; while (val < 0) val += MOD; } else static if (op == "*") { val = val * rhs.val % MOD; while (val < 0) val += MOD; } else static if (op == "/") { val = val * rhs.inv().val % MOD; while (val < 0) val += MOD; } return this; } ModInt opOpAssign(string op, T)(T rhs) pure nothrow if (isIntegral!T && (op == "+" || op == "-" || op == "*" || op == "/")) { static if (op == "+") { val += rhs; while (val >= MOD) val -= MOD; } else static if (op == "-") { val -= rhs; while (val < 0) val += MOD; } else static if (op == "*") { val = val * rhs % MOD; while (val < 0) val += MOD; } else static if (op == "/") { val = val * Mint(rhs).inv().val % MOD; while (val < 0) val += MOD; } return this; } // 二項演算子 ModInt opBinary(string op, T)(T rhs) pure nothrow if (is(T : ModInt) && (op == "+" || op == "-" || op == "*" || op == "/")) { auto r = this; return r.opOpAssign!op(rhs); } ModInt opBinary(string op, T)(T rhs) pure nothrow if (isIntegral!T && (op == "+" || op == "-" || op == "*" || op == "/")) { auto r = this; return r.opOpAssign!op(rhs); } ModInt opBinaryRight(string op, T)(T lhs) pure nothrow if (isIntegral!T && (op == "+" || op == "-" || op == "*" || op == "/")) { ModInt r = lhs; return r.opOpAssign!op(this); } // 単項演算子 ModInt opUnary(string op)() pure nothrow if (op == "++" || op == "--") { static if (op == "++") { ++val; if (val == MOD) val = 0; } else static if (op == "--") { if (val == 0) val = MOD; --val; } return this; } ModInt opUnary(string op)() pure const nothrow if (op == "-") { if (val == 0) return Mint(0, false); else return Mint(MOD - val, false); } // 等号演算子 bool opEquals(ref const ModInt rhs) @safe pure const nothrow { return val == rhs.val; } bool opEquals(T)(const T rhs) @safe pure const nothrow if (isIntegral!T) { return val == rhs; } // 累乗 ModInt pow(T)(T n) pure const if (isIntegral!T) { ModInt x = n >= 0 ? this : inv(), r = 1; while (n) { if (n & 1) r *= x; x *= x; n >>= 1; } return r; } // MODに関する逆元 ModInt inv() pure const { long a = val, b = MOD, u = 1, v = 0; while (b) { long t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v); } return ModInt(u); } string toString() pure nothrow const { return val.to!string; } // 連想配列のためのハッシュ hash_t toHash() const @safe pure nothrow { // MMIX by Donald Knuth return cast(hash_t)(6_364_136_223_846_793_005L * val + 1_442_695_040_888_963_407L); } } immutable MOD = 10L ^^ 9 + 7; alias Mint = ModInt!MOD; immutable N = 1_000_005; // MODを法とする階乗とその逆元 Mint[] fact, inv, factInv; void init() { fact = new Mint[](N + 1), inv = new Mint[](N + 1), factInv = new Mint[](N + 1); fact[0] = fact[1] = 1L; inv[1] = 1L; factInv[0] = factInv[1] = 1L; foreach (i; 2L .. N + 1) { fact[i] = i * fact[i - 1]; inv[i] = -inv[MOD % i] * (MOD / i); factInv[i] = inv[i] * factInv[i - 1]; } } void main() { init(); // 入力 int K = readln.chomp.to!int; if (K == 1) { writeln(1); return; } auto C = readln.split.to!(long[]); // 答えの計算 long T = C.sum; long g = C[0]; foreach (i; 1 .. K) g = gcd(g, C[i]); Mint total = 0; long[] V; for (long i = 1; i * i <= g; i++) { if (g % i) continue; V ~= T / i; if (i * i != g) V ~= T / (g / i); } V.sort; auto num = new Mint[](N); foreach (i, x; V) { num[x] = fact[x]; foreach (j; 0 .. K) num[x] *= factInv[C[j] / (T / x)]; foreach (j; 0 .. i) if (x % V[j] == 0) num[x] -= num[V[j]]; total += num[x] * Mint(x).inv(); } // 答えの出力 writeln(total); }