あるエンジニアのAtCoder奮闘記

東京都港区にあるアミフィアブル株式会社のエンジニアが、AtCoderで解いた問題について振り返ったりしていく会社公認のブログです。

AtCoder ABC 133 E - Virus Tree 2

irisruneです。個人的に以前のABCの4完が最近のABCの5完に相当するように思えるのですが、誰か統計取ったりしているのでしょうか。

問題

atcoder.jp

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点問題ということやグラフ(正確には木)問題ということもあってかなり難しく捉えがちですが実はかなり単純で、幅優先探索を行うことを考えると、

  • 適当な点を始点として考えると、始点で選べる色はK通り。
  • 始点から1番目に選んだ点で選べる色はK-1通り。
  • 始点から2番目に選んだ点で選べる色は、
    • 始点と隣接している場合はK-2通り。
    • 始点から1番目に選んだ点と隣接している場合はK-2通り。
  • 始点から3番目に選んだ点で選べる色は、
    • 始点と隣接している場合はK-3通り。
    • 始点から1番目に選んだ点と隣接している場合はK-3通り。
    • 始点から2番目に選んだ点と隣接している場合はK-2通り。

…というように組み合わせの数が決まっていきます。少し整理すると、

  • 始点に隣接する点については、順番にK-1,K-2,\ldots通りの色を選べる。
  • それ以外の点に隣接する点については、順番にK-2,K-3,\ldots通りの色を選べる。
    • 当然ながら、親となる点は含まない。

となるので、それに従って色を選ぶ組み合わせの数を計算することができます。途中で負の値が出現するケースも考えられますが、その場合は0も出現するため出力結果が0となるだけで問題はないです。

計算量はO(N)程度なので、順列の組み合わせ数を求めて計算に用いる場合(おそらくO(N+K))に比べると計算量は少なく済むと思います。公式解説のコードでは順列の組み合わせを用いていないようですが。

余談ですが、深さ優先探索で解く場合には順列の組み合わせ数を用いざるを得ないとは思います。

雑記

  • 変数について、k_nextはまだしもk_は流石にネタが切れすぎでしょうと見直して思いました。