LC 3317. 安排活动的方案数
https://leetcode.cn/problems/find-the-number-of-possible-ways-for-an-event/description/
拆解成为下面几个问题
- 最后选择了i个节目的话,那么评分组合可以有 \(y^i\) 种可能
- 选择i个节目的组合数量是 \(C(x, i)\)
- 最后的问题就是将n个人分配到i个节目,并且确保每个节目至少有一个人的可能性是多少
假设这个函数是\(F(n,i)\).
- 如果随机分配的话,那么可以有 \(i^n\) 种可能性,但是某些节目可能是空
- x个节目是空数量是 \(F(n,i-x) * C(i,x)\).
- 枚举所有x的可能性,这样计算每个 \(F(n,i)\) 的时间复杂是O(N)
class Solution: def numberOfWays(self, n: int, x: int, y: int) -> int: def dfs(i, prog): ans = 0 if i == n: print(prog, y ** len(prog)) return y ** len(prog) for j in range(x): prog[j] += 1 ans += dfs(i + 1, prog) prog[j] -= 1 if prog[j] == 0: del prog[j] return ans # from collections import Counter # return dfs(0, Counter()) # sum{i=0..x}(y ** i * C(x, i) * F(n, i)) # F(n, i) 把n个人配置到i个项目里面,并且至少去确保每个都存在一个 # F(n, i) = (i**n) - F(n,i-1)*C(i,i-1) - F(n,i-2)*C(i,i-2).... # F(n, 1) = 1 MOD = 10 ** 9 + 7 # a ^ b def pow(a, b): ans = 1 t = a while b: if b & 0x1: ans = (ans * t) % MOD t = (t * t) % MOD b = b >> 1 return ans C = [[0] * (x + 1) for _ in range(x + 1)] C[0][0] = 1 for i in range(1, x + 1): for j in range(0, i + 1): C[i][j] = C[i - 1][j] + (C[i - 1][j - 1] if j > 0 else 0) C[i][j] %= MOD F = [0] * (x + 1) F[1] = 1 for i in range(2, x + 1): acc = 0 for j in reversed(range(i)): acc += (F[j] * C[i][j]) % MOD acc = acc % MOD F[i] = (pow(i, n) - acc) % MOD ans = 0 for i in range(0, x + 1): r = pow(y, i) * C[x][i] * F[i] # print(i, r, '===>', pow(y, i), C[x][i], F[i]) ans = (ans + r) % MOD return ans