題目連結:Codeforces Round #216 C。給定一棵 N ≤ 10^5 節點的樹(根為 1),部分邊標記為需修復 (權值 2) 或不需 (權值 1)。選擇一組節點,使得從這些節點到根的路徑覆蓋所有需修復邊,且集合大小最小。輸出任意最小集合 (SPJ)。
sum[u]。sum[u] ≥ 1 (子樹已有選點) → 當前路徑已被覆蓋。sum[u] == 0 (子樹無選點) → 必須選當前節點,加入答案,返回 1 向上傳遞「已覆蓋」。
#include
#include
#include
#define MAXN 101000
struct Edge { int to, w, next; } e[MAXN << 2];
int head[MAXN], ecnt;
int vis[MAXN], sum[MAXN], ans[MAXN], ancnt;
void add_edge(int u, int v, int w) {
e[ecnt] = (struct Edge){v, w, head[u]};
head[u] = ecnt++;
}
int dfs(int u, int w) {
vis[u] = 1;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (vis[v]) continue;
sum[u] += dfs(v, e[i].w);
}
if (w == 2 || sum[u] >= 1) {
if (sum[u] == 0) ans[ancnt++] = u;
return 1;
}
return 0;
}
int main() {
int n;
while (scanf("%d", &n) == 1) {
ecnt = ancnt = 0;
memset(head, -1, sizeof(head));
memset(sum, 0, sizeof(sum));
memset(vis, 0, sizeof(vis));
for (int i = 1; i < n; ++i) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
add_edge(a, b, c);
add_edge(b, a, c);
}
vis[1] = 1;
dfs(1, 0);
printf("%d\n", ancnt);
for (int i = 0; i < ancnt; ++i)
printf("%d%c", ans[i], i == ancnt - 1 ? '\n' : ' ');
}
return 0;
}
O(N),每條邊訪問一次。O(N),鄰接表 + 遞歸棧 (深度 ≤ N,需注意棧溢位,可改迭代或增大棧)。sum[u] ≥ 1 表示子樹已有人選中,避免重複選點。