宣传一下 算法提高课整理
CSDN个人主页:更好的阅读体验
原题链接
CF461B
题目描述
一棵树有 n n n 个节点, n − 1 n − 1 n−1 条边。树上的节点有两种:黑,白节点。
Tyk 想断掉一些边把树分成很多部分。
他想要保证每个部分里面有且仅有一个黑节点。
请问他一共有多少种的方案?
输入
第一行一个数字 n n n,表示树的节点个数。
第二行一共 n − 1 n − 1 n−1 个数字 p 0 , p 1 , p 2 , p 3 , . . . , p n − 2 p_0, p_1, p_2, p_3, ..., p_{n−2} p0,p1,p2,p3,...,pn−2, p i p_i pi 表示第 i + 1 i + 1 i+1 个节点和 p i p_i pi 节点之间有一条边。注意,点的编号是 0 0 0 到 n − 1 n − 1 n−1。
第三行一共 n n n 个数字 x 0 , x 1 , x 2 , x 3 , . . . , x n − 1 x_0, x_1, x_2, x_3, ..., x_{n−1} x0,x1,x2,x3,...,xn−1。如果 x i x_i xi 是 1 1 1,表示 i i i 号节点是黑的
如果 x i x_i xi 是 0 0 0,表示 i i i 号节点是白的。
输出
输出一个数字,表示总方案数。答案对 1 0 9 + 7 10^9 + 7 109+7 取模。
样例输入
3
0 0
0 1 1
样例输出
2
思路
一眼丁真,鉴定为 树形DP。
- 状态表示
f
u
,
0
/
1
f_{u,0/1}
fu,0/1:
- 集合: 在编号为 u u u 的节点所在连通块中,没有黑点(0) | 只有 u u u 一个黑点(1)
- 属性: 方案数 Count \text{Count} Count
- 状态计算:
- 对于
f
u
,
1
f_{u,1}
fu,1,讨论它子节点
j
j
j 进行划分:
- 点 j j j 不是黑色点,此时点 j j j 可以被划分到点 u u u 所在连通块: f u , 1 × f j , 0 f_{u,1}\times f_{j,0} fu,1×fj,0;
- 点
j
j
j 是黑色点:
- 点 u u u 是黑色点,此时点 u u u 和点 j j j 分属两个不同连通块。 f u , 1 × f j , 1 f_{u,1}\times f_{j,1} fu,1×fj,1;
- 点 u u u 不是黑色点,此时点 u u u 可以被划分到点 j j j 所在连通块。 f u , 0 × f j , 1 f_{u,0}\times f_{j,1} fu,0×fj,1;
- 对于
f
u
,
0
f_{u,0}
fu,0 同理进行划分,不过注意此时点
u
u
u 一定不是黑色点,不然不符合状态表示:
- 点 j j j 不是黑色点,此时任意划分: f u , 0 × f j , 0 f_{u,0}\times f_{j,0} fu,0×fj,0;
- 点 j j j 是黑色点,此时点 u u u 划分到点 j j j 所在连通块。 f u , 0 × f j , 1 f_{u,0}\times f_{j,1} fu,0×fj,1;
- 初始化点
u
u
u :
- 当点 u u u 是黑色点时: f u , 1 ← 1 f_{u,1}\leftarrow 1 fu,1←1;
- 当点 u u u 不是黑色点时: f u , 0 ← 1 f_{u,0}\leftarrow 1 fu,0←1.
- 对于
f
u
,
1
f_{u,1}
fu,1,讨论它子节点
j
j
j 进行划分:
贴出核心代码:
void dfs(int u, int fa)
{
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
dfs(j, u);
f[u][1] = ((f[u][1] * f[j][0] % mod + f[u][1] * f[j][1] % mod) % mod + f[u][0] * f[j][1] % mod) % mod;
f[u][0] = (f[u][0] * f[j][0] % mod + f[u][0] * f[j][1] % mod) % mod;
}
}
最后,如果觉得对您有帮助的话,点个赞再走吧!