牛牛染颜色
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
牛牛最近得到了一颗树,根是 1 号节点,他想要把这颗树染色。
每个节点可以染成白色和黑色,牛牛认为一种染色方案是好的当且仅当任意两个黑点的 lca(最近公共祖先)的颜色也是黑色的。
求一共有多少种好的染色的方案。
输入描述:
第一行一个整数 n,表示这棵树有 n个节点。 接下来 n-1行,每行两个整数 u,v 表示 u,v之间有一条边。
输出描述:
一行一个整数,表示所有合法的点的集合数量。
示例1
输入
4
1 2
2 3
2 4
输出
14
说明
共计14个集合满足题意。
注意:空集也算进答案里面!
示例2
输入
6
1 2
1 3
1 4
2 5
5 6
输出
42
备注:
1≤n≤10 6 ,1≤u,v≤n
题意:
一个有n个节点的树,(其中根节点是1),每个节点可以染色为黑与白,如果有两个节点都是黑,他们的最近公共祖先也必须是黑,问有多少种涂色方案?
题解:
树形dp问题
我们可以用dp[x][0/1]来表示树上的节点x已经被染成黑色或者是白色的方案数(黑用1,白用0)
因为题目只对黑色有限制条件,所以如果x为黑色,x的子节点可以是黑色也可以是白色,但是如果x为白色,x的子节点只能是白色,或者是一个黑色,因为如果有两个黑色,x作为他俩的最近公共祖先也必须是黑色。
我们在处理时仅考虑染成黑色就行,因为黑色除外的都要染成白色。1是染成黑色,0为白色(也可以理解成不处理)
我们根据上述分析可以列出式子:
dp [x ] [ 0 ]= d p [ x] [ 0 ] + d p[ y ] [ 1 ]+ d p [ y ][ 0 ] - 1
x点不染色的情况是,x的子节点只能是白色,或者是一个黑色(因为存在空集所以减一)
dp [x ] [ 1 ] = dp[ x] [ 1 ] * dp [y ] [1 ] +dp[ x] [ 1 ] * d p[ y ] [ 0 ]
x染为黑,x的子节点可以是黑色也可以是白色,种类数量相乘
最后推到根节点1
答案就是dp[1][1]+dp[1][0]
核心代码:
void dpp(int x,int fa){
dp[x][0]=1;
dp[x][1]=1;//先对当前节点初始化
int y;
for(int i=head[x];~i;i=edge[i].next)
{
y=edge[i].y;
if(y==fa)continue;//如果找到本身则跳过
dfs(y,x);//顺着树继续向下找
dp[x][0]=(dp[x][0]+dp[y][1]+dp[y][0]-1)%mod;
dp[x][1]=dp[x][1]*(dp[y][1]+dp[y][0])%mod;
}
}
printf("%d",(dp[1][0]+dp[1][1])%mod);
每日一题倒是出现了好几个树形dp的题