固定起点后,每个点只需要排列它的孩子访问顺序,最后可化成度数公式。
P13020 [GESP202506 八级] 遍历计数
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
枚举所有 DFS 顺序会指数爆炸,n 到 1e5 不可能。
二、核心观察
以 s 为根时,根贡献 deg(s)!,其他点贡献 (deg(u)-1)!。
为什么不丢答案:DFS 在树上进入一个子树后必须完整返回,唯一自由度就是各孩子子树的访问顺序。
deg[u]
阶乘贡献
总公式
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
根和非根贡献为什么不同?
先判断,再点按钮。
模数是多少?
先判断,再点按钮。
五、手推结果
| 点类型 | 孩子数 | 贡献 |
|---|---|---|
| 根 s | deg(s) | deg(s)! |
| 非根 u | deg(u)-1 | (deg(u)-1)! |
| 总和 | 所有起点 | 2(n-1)*P |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
if (n == 1) {
cout << 1 << endl;
return 0;
}
vector<int> deg(n + 1, 0);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
deg[u]++;
deg[v]++;
}
// 预处理阶乘
vector<ll> fact(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; i++) {
fact[i] = (fact[i - 1] * i) % MOD;
}
ll p = 1;
for (int i = 1; i <= n; i++) {
// 每个点贡献 (deg[i]-1)!
if (deg[i] > 0) {
p = (p * fact[deg[i] - 1]) % MOD;
}
}
// 最终结果 = 2 * (n-1) * p
ll ans = (2LL * (n - 1)) % MOD;
ans = (ans * p) % MOD;
cout << ans << endl;
return 0;
}