轮舞前夕

题目大意及模型转换

一棵由N个节点组成的有根树,根节点为1。现在在一个节点上放一盏灯,将会照亮该节点以及该节点的儿子与父亲。输出最少放多少灯,有多少种方案,能让所有节点被照亮。

简单的树形DP

可以设f[i]表示在节点i放灯,整个以i为根的子树都被照亮至少放多少灯。设g[i]表示在节点i不放灯,节点i被儿子照亮,整个以i为根的子树都被照亮至少放多少灯。设h[i]表示在节点i不放灯,节点i应被父亲照亮,目前整个以i为根的子树除了i都被照亮至少放多少灯。那么这三个转移都显然。
f[i]=jimin(f[j],g[j],h[j])+1
g[i]=kimin(f[k]+jijkg[j])
h[i]=jig[j]
答案显然是f[1]与g[1]的较小值
为了省时间,设 dif[i]=min(f[j]g[j])[ji]
那么我们先求出所有儿子的g值和,然后若dif[i]>=0,g[i]需加上dif[i]
为什么呢?自己想想吧

求方案数

设fs[i],gs[i],hs[i]分别对应三种情况的方案数
对于fs[i]和hs[i]以及dif[i]<0时的gs[i]非常容易得到
现在讨论如何获得gs[i]
dif[i]=0时,我们将所有f[j]=g[j]的儿子方案数表示为fs[j]+gs[j],然后统计方案数,由于这种情况下我们需要选择至少一个儿子让他亮灯,因此要减去所有儿子g值之积
dif[i]>0时,设pre[i]表示前i个儿子方案数总积,sa[i]表示后i个儿子方案数总积。然后枚举一个j,如果f[j]-g[j]=dif[i],那么gs[i]就加上pre[j-1]*sa[j+1]*fs[j]

注意

有些时候是加法原理,处理fs和hs时要注意(以及dif[i]<0时的gs)
深搜会爆,因此做广搜,然后将宽搜序列倒过来访问
你想打人工栈,我也没有办法

你可能感兴趣的:(dp)