結果
| 問題 |
No.125 悪の花弁
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-09-23 22:05:09 |
| 言語 | D (dmd 2.109.1) |
| 結果 |
AC
|
| 実行時間 | 72 ms / 5,000 ms |
| コード長 | 5,419 bytes |
| コンパイル時間 | 6,134 ms |
| コンパイル使用メモリ | 205,332 KB |
| 実行使用メモリ | 42,528 KB |
| 最終ジャッジ日時 | 2025-09-23 22:05:16 |
| 合計ジャッジ時間 | 7,639 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 6 |
コンパイルメッセージ
/home/linuxbrew/.linuxbrew/opt/dmd/include/dlang/dmd/std/numeric.d(2999): Warning: cannot inline function `std.numeric.gcdImpl!ulong.gcdImpl`
ソースコード
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);
}