21.10.11模拟 胖哥的树


一棵 n 个节点的树,其中有一些节点是黑色的,另外一些节点是白色的。考虑这棵树上的一个 k 条边的边集。如果将这 k 条边从树上删除,则将会把这棵树分成 K+1 个联通块。现在,胖哥想知道,存在多少种这样的边集,使得删除这些边后,每个联通块只有一个黑色的节点。

设 dp[i][0]代表 i 这个联通块没有黑点的方案数,dp[i][1]代表有一个黑点的方案数。
假设父亲节点为 u,考虑儿子节点的情况:
如果儿子节点是个有黑点的,父亲节点也有黑点,那么只能分裂开,不能合并在一起,只对 dp[u][1]有贡献
如果儿子节点是个有黑点的,父亲节点没有黑点,那么可以分裂也可以合并,对 dp[u][1]和 dp[u][0]都有贡献
如果儿子节点是个没有黑点的,父亲节点有黑点,那么必须和父亲合并,对 dp[u][1]有贡献
如果儿子节点是个没有黑点的,父亲节点也没黑点,那么必须和父亲合并,对 dp[u][0]有贡献

边界状态:
如果第 i 个节点为黑色,那么 dp[i][0]=0,dp[i][1]=1;
如果第 i 个节点为白色,那么 dp[i][0]=1,dp[i][1]=0

int color[N];
lxl f[2][N];//f_0_i±íê? i?ù?úá?í¨?é??óDoúμ?μ?·?°?êy f_1_i′ú±íò???á?í¨?éóDoúμ?μ?1±?×êy

inline void dfs(int x,int fa){
	f[color[x]][x]=1;
	repg(x){
		int y(G.ver[i]);
		if(y==fa) continue;
		dfs(y,x);
		f[1][x]=(f[1][y]*f[0][x]+f[1][x]*f[0][y]+f[1][x]*f[1][y])%mod;
		f[0][x]=(f[0][y]*f[0][x]+f[0][x]*f[1][y])%mod;
	}
}

int main() {
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	read(n);
	int fa;
	rep(i, 2, n) {
		read(fa);
		fa++;
		G.add(fa, i);
		G.add(i, fa);
	}
	rep(i, 1, n) {
		read(color[i]);	
	}
	dfs(1,-1);
	out(f[1][1],'\n');
	return 0;
}