voiddfs1(int u, int fa){ // 第一遍 DFS:求重儿子 sz[u] = 1; for (int j = head[u]; j != 0; j = edge[j].next) { int v = edge[j].to; if (v == fa) continue; dfs1(v, u), sz[u] += sz[v]; if (sz[v] > sz[son[u]]) son[u] = v; } } voiddfs2(int u, int fa, bool keep){ // 第二遍 DFS:dsu on tree // keep 表示是否保留 u 的影响 for (int j = head[u]; j != 0; j = edge[j].next) { int v = edge[j].to; if (v != fa && v != son[u]) dfs2(v, u, false); // 遍历轻儿子 } if (son[u]) dfs2(son[u], u, true); // 遍历重儿子 for (int j = head[u]; j != 0; j = edge[j].next) { int v = edge[j].to; if (v == fa || v == son[u]) continue; // 加入 v 子树的影响 // 常常要处理 dfs 序后将 v 子树中的节点全部遍历一次 } // 计算 u 子树的答案 if (!keep) { // 清空 u 的影响 // 常常要处理 dfs 序后将子树中的节点全部删除一次 } }
有一种好写的方法是利用 set 实现,但复杂度会变成 O(nlog2n):
1 2 3 4 5 6 7 8 9 10 11 12 13 14
set<int> s[N]; voiddfs(int u, int fa){ bool keep = true; // 是否保留 u 子树的影响 for (int j = head[u]; j != 0; j = edge[j].next) { int v = edge[j].to; if (v == fa) continue; dfs(v, u); if (s[u].size() < s[v].size()) swap(u, v); // set 启发式合并 // 将 v 的影响合并到 u 上并计算 keep for (int i : s[v]) s[u].emplace(i); } // 计算 u 子树的答案 if (!keep) s[u].clear(); // 清空 }