LC 1916. 统计为蚁群构筑房间的不同顺序
因为只有N个节点,并且只有N-1条边,同时保证最后是完全联通的,所以结构上只能是一个树。
假设一个节点有3个孩子ABC,每个孩子分别有Na, Nb, Nc种访问顺序,那么这棵树有:
- 首先不考虑A, B, C各自节点穿插,那么就有Na * Nb * Nc种访问顺序
- 如果考虑之间的穿插,不考虑ABC内部顺序的话,那么有(A+B+C)!种访问顺序
- 如果考虑内部顺序的话,那么还要除去 (A! * B! * C!), 因为对A内来说顺序不能打乱,已经有Na种了。
这个计算过程涉及到除法取模,所以要用到 费马小定理. 阶乘以及阶乘的倒数必须预先计算出来。整个过程分为两步:构建树和遍历树。
#!/usr/bin/env python # coding:utf-8 # Copyright (C) dirlt from typing import List from collections import Counter, defaultdict, deque from functools import lru_cache import heapq class Tree: def __init__(self, idx): self.idx = idx self.child = [] self.n = 1 self.val = 1 class Solution: def waysToBuildRooms(self, prevRoom: List[int]) -> int: # n个节点并且n-1条边,并且0可以访问到所有房间,说明是个树结构 n = len(prevRoom) ind = [0] * n for x in prevRoom: if x == -1: continue ind[x] += 1 # build tree by top sort. from collections import deque dq = deque() for i in range(n): if ind[i] == 0: dq.append(i) trees = [] for i in range(n): trees.append(Tree(i)) while dq: x = dq.popleft() p = prevRoom[x] if p == -1: continue t = trees[x] pt = trees[p] pt.child.append(t) ind[p] -= 1 if ind[p] == 0: dq.append(p) MOD = 10 ** 9 + 7 fac = [0] * (n + 1) rev = [0] * (n + 1) t = 1 for i in range(1, n + 1): t = (t * i) % MOD fac[i] = t def pow(x, y): t = 1 while y: if y & 0x1: t = (t * x) % MOD x = (x * x) % MOD y = y >> 1 return t for i in range(1, n + 1): rev[i] = pow(fac[i], MOD - 2) # print(fac, rev) def compute(t): if len(t.child) == 0: return # 假设有3个子节点,A, B, C # 每个节点下面排列分别是Na, Nb, Nc # 那么总排列是 (A+B+C)! / (A! * B! * C!) * (Na * Nb * Nc) n = 0 b = 1 for c in t.child: compute(c) n += c.n b = (b * rev[c.n] * c.val) % MOD t.n = n + 1 t.val = (fac[n] * b) % MOD return compute(trees[0]) ans = trees[0].val return ans