<-- home

AtCoder ABC 156

ABC156 の自分用メモ

A B C D E F
o o o x x x

A, B, Cは問題なく解くことができたが、Dで引っかかった。

D問題

https://atcoder.jp/contests/abc156/tasks/abc156_d

aとbを考慮せずn種類の花から1本以上選んで花束を作る方法は

で求められる(あるいはn種類の花それぞれについて選ぶ選ばないを考えて、すべてを選ばない場合を引いて)。そこからを引いた値が求める答えとなることはわかったが、 という条件を見て、が制限時間以内に計算できないと判断してしまった。解いてる最中は累乗と階乗が頭でごっちゃになっていたり、累乗etcのアルゴリズムについての理解不足が原因。以下、累乗、累乗の逆元、二項係数それぞれについてまとめる。

累乗

について求めたいとする。愚直に求めると

p = 1
for 1...16 {
    p *= 3;
}

のように となる。しかし、

と計算できることを考慮すると、

p = 3
for i = 1...4 {
    p = p * p
}

で求めることができ、この場合となる。求めたい累乗は必ずしも2のn乗とはならないから一般的にを求めるときに、この方法を応用できないかを考える。で計算できることがわかっているので、nを2の累乗の和に分解できれば、

のように計算できる。累乗和への分解は、nを二進数にしたとき、i桁目が1かどうかを調べることでできる。これを踏まえると、以下のコードでで計算できる。

ans = 1
p = 3
while (n > 0) {
    if (n & 1) ans *= p
    p = p * p
    n = n >> 1
}

も同様に求められる。今回のD問題のようにmodをとる必要がある問題も多いが、累乗は乗算しかおこなわないので、各計算ごとにmodをとればも計算できる。

累乗の逆元

modを取らない場合は、上の方法で累乗を求めて割ればいいだけなので、におけるの逆元を求める方法についてまとめる。以下pを法とする。求める方法は2つある。

  • Fermatの小定理を使う
  • 拡張ユークリッド互除法を使う

どちらも計算量はとなる。前者の場合、Fermatの小定理からaの逆元はとなる。は上の累乗を求めるアルゴリズムを用いてで求められる。したがって、累乗の逆元を求めたい場合は、累乗を求めて、その逆元を求めればよいので、となる。

二項係数

今回の問題では、なので、の分母、分子はそれぞれで計算できるので間に合う。しかし、aがより大きいとき、階乗を効率的に計算する方法があるのか気になったので調べたところで計算する方法があるらしい[2]。

参考