题目大意:给定一个无根树,给每条边黑白染色,求出每个点为根时,其他点到根的路径上至多有一条黑边的染色方案数,模$1e9+7$。
题解:树形$DP$不难想到,记$f_u$为以$1$为根时,以$u$为根的子树的方案数,$f_u=prodlimits_{vin son_u}(f_v+1)$
换根也很简单。
但是这题卡模数,换根时要求逆元,其中$f_u$可能等于$1e9+6$,加一后变成$0$,无法求逆。可以求前缀积和后缀积转移
卡点:原$dp$写错
C++ Code:
#include <cstdio> #include <vector> #include <cctype> namespace R { int x, ch; inline int read() { ch = getchar(); while (isspace(ch)) ch = getchar(); for (x = ch & 15, ch = getchar(); isdigit(ch); ch = getchar()) x = x * 10 + (ch & 15); return x; } } using R::read; #define maxn 200010 const int mod = 1e9 + 7; int head[maxn], cnt; struct Edge { int to, nxt; } e[maxn << 1]; inline void add(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; } int n; int f[maxn], g[maxn], ans[maxn], l[maxn], r[maxn]; void dfs(int u, int fa = 0) { f[u] = 1; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v != fa) { dfs(v, u); f[u] = static_cast<long long> (1 + f[v]) * f[u] % mod; } } } void dfs1(int u, int fa = 0) { std::vector<int> S; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v != fa) S.push_back(v); } if (S.size()) { l[*S.begin()] = g[u]; for (std::vector<int>::iterator it = S.begin() + 1; it != S.end(); it++) { l[*it] = static_cast<long long> (l[*(it - 1)]) * (f[*(it - 1)] + 1) % mod; } r[*(S.end() - 1)] = 1; if (S.begin() + 1 != S.end()) { for (std::vector<int>::iterator it = S.end() - 2; true; it--) { r[*it] = static_cast<long long> (r[*(it + 1)]) * (f[*(it + 1)] + 1) % mod; if (it == S.begin()) break; } } } for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v != fa) { g[v] = (static_cast<long long> (l[v]) * r[v] + 1) % mod; ans[v] = static_cast<long long> (g[v]) * f[v] % mod; dfs1(v, u); } } } int main() { n = read(); for (int i = 1, x; i < n; i++) { x = read(); add(i + 1, x); add(x, i + 1); } dfs(1); ans[1] = f[1]; g[1] = 1; dfs1(1); for (int i = 1; i <= n; i++) { printf("%d", ans[i]); putchar(i == n ? ' ' : ' '); } return 0; }