AtCoder ABC 133 E - Virus Tree 2
irisruneです。個人的に以前のABCの4完が最近のABCの5完に相当するように思えるのですが、誰か統計取ったりしているのでしょうか。
問題
500点問題の割にアルゴリズムも実装もかなり軽めで、発想系の問題だと思いました。
import sys import collections as col import numpy as np MOD = 1000000007 def input(): return sys.stdin.readline().rstrip() def main(): n, k = [int(e) for e in input().split()] adj = [[] for _ in range(n+1)] for _ in range(n-1): u, v = [int(e) for e in input().split()] adj[u].append(v) adj[v].append(u) ans = 1 queue = col.deque([(1, 0, k)]) while len(queue) > 0: u, p, k_ = queue.popleft() ans = (ans * k_) % MOD if p == 0: k_next = k - 1 else: k_next = k - 2 for v in adj[u]: if v == p: continue queue.append((v, u, k_next)) k_next -= 1 print(ans) main()
500点問題ということやグラフ(正確には木)問題ということもあってかなり難しく捉えがちですが実はかなり単純で、幅優先探索を行うことを考えると、
- 適当な点を始点として考えると、始点で選べる色は通り。
- 始点から1番目に選んだ点で選べる色は通り。
- 始点から2番目に選んだ点で選べる色は、
- 始点と隣接している場合は通り。
- 始点から1番目に選んだ点と隣接している場合は通り。
- 始点から3番目に選んだ点で選べる色は、
- 始点と隣接している場合は通り。
- 始点から1番目に選んだ点と隣接している場合は通り。
- 始点から2番目に選んだ点と隣接している場合は通り。
…というように組み合わせの数が決まっていきます。少し整理すると、
- 始点に隣接する点については、順番に通りの色を選べる。
- それ以外の点に隣接する点については、順番に通りの色を選べる。
- 当然ながら、親となる点は含まない。
となるので、それに従って色を選ぶ組み合わせの数を計算することができます。途中で負の値が出現するケースも考えられますが、その場合は0も出現するため出力結果が0となるだけで問題はないです。
計算量は程度なので、順列の組み合わせ数を求めて計算に用いる場合(おそらく)に比べると計算量は少なく済むと思います。公式解説のコードでは順列の組み合わせを用いていないようですが。
余談ですが、深さ優先探索で解く場合には順列の組み合わせ数を用いざるを得ないとは思います。
雑記
- 変数について、k_nextはまだしもk_は流石にネタが切れすぎでしょうと見直して思いました。