进行一个 DP.设 fu,0/1/2/3 表示考虑了 u 子树,u 没有被擦除 / u 被 ≺f 的儿子擦除 / u 被 f 擦除 / u 被 ≻f 的儿子擦除,其中 f 代表点 u 的父亲.
分 4 类转移:
考虑转移 0 情况.这时 u 的儿子必定都被擦除,且儿子 v 的擦除时间一定不后于 (u,v) 这条边被考虑.这是因为考虑 (u,v) 这条边时,为了保全 u 不被擦除,v 一定会被擦.那么有转移
fu,0=v∏(fv,1+fv,2)
考虑转移 1 情况.我们可以从儿子中钦定一个 ≺f 的 v 作为擦掉 u 的点.组合其他点的方案时,如果存在 ≺v 的儿子 w 不被擦除或者在考虑边 (u,w) 之后被擦,那么 u 会被 w 擦而不是被 v 擦,这样的状态不合法.由于在考虑 (u,v) 时 u 被擦了,那么 ≻v 的儿子显然就不能被 u 擦了.那么有转移
inlineintrd(){ int x = 0, f = 1, c = getchar(); while (((c - '0') | ('9' - c)) < 0) f = c != '-', c = getchar(); while (((c - '0') | ('9' - c)) > 0) x = x * 10 + c - '0', c = getchar(); return f ? x : -x; }
using pii = std::pair<int, int>;
using i64 = longlong; const i64 P = 998244353;
constint N = 2e5;
int n; std::vector<pii> g[N + 5];
int fid[N + 5]; voidpre(int u, int fa){ for (auto [v, id] : g[u]) { if (v == fa) continue; fid[v] = id, pre(v, u); } }
i64 f[N + 5][4]; voiddfs(int u, int fa){ for (auto [v, _] : g[u]) if (v != fa) dfs(v, u);
staticint V[N + 5]; int tp = 0; for (auto [v, _] : g[u]) if (v != fa) V[++tp] = v;
static i64 pre[N + 5], suf[N + 5]; pre[0] = suf[tp + 1] = 1; for (int i = 1; i <= tp; i++) { int v = V[i]; pre[i] = (f[v][1] + f[v][2]) % P; suf[i] = (f[v][0] + f[v][1] + f[v][3]) % P; } for (int i = 1; i <= tp; i++) (pre[i] *= pre[i - 1]) %= P; for (int i = tp; i >= 1; i--) (suf[i] *= suf[i + 1]) %= P;
intmain(){ n = rd(); for (int i = 1; i <= n - 1; i++) { int u = rd(), v = rd(); g[u].emplace_back(v, i); g[v].emplace_back(u, i); } fid[1] = n, pre(1, 0), dfs(1, 0); printf("%lld\n", (f[1][0] + f[1][1]) % P); return0; }