P3177 [HAOI2015] 树上染色 题解
前言
传送门
看过《指南》的同学应该都有印象,与其说这道题是个多重背包,其实不如说它是个分组背包。
题目转化为个体贡献这个trick不加赘述,其它题解说的很清楚。其实我觉得只要理解了它是个分组背包,可以方便思考很多,也不会出现那么多问题。
正文
我们先给
讲解答前要搞明白的一个幺蛾子:而我们转移时的决策其实是如何分配这
如果幺蛾子看着有点懵,可以先看解答。为什么说它是分组背包?因为每一个子树就是一组,分配给这个子树多少黑点就是物品,这些物品显然是互斥的,因为每次决策你只能分给一个子树定值数量的黑点。那么根据分组背包的嵌套结构,最外层应是枚举每一组(即枚举每个儿子,这里肯定要递归,因为决策要用到儿子的结果),第二层应是倒序枚举容量(保证状态是由前一组转移过来的),第三层则是枚举物品。
下面是核心的dp函数:(
void dp(int u,int fa){
sze[u]=1;
f[u][0]=0;
//边界注意,没有枚举组之前,只有分配0个黑点是合法的
//注意,上文有提到,我这里的解法是先不分配u的颜色的,其实更有助于理解
for(int i=head[u];i;i=e[i].nxt){//枚举每一组
int v=e[i].to;
if(v==fa) continue;
dp(v,u);
sze[u]+=sze[v];
for(int V=min(k,sze[u]);V>=0;V--){//倒序枚举容量
for(int j=max(0LL,V-sze[u]);j<=min(V,sze[v]);j++){//每组只能选一个
if(f[u][V-j]==-1) continue;//这句意义在于枚举前一组时f[u][V-j]不一定计算过,该情况不合法要跳掉
f[u][V]=max(f[u][V],f[u][V-j]+f[v][j]+(j*(k-j)+(sze[v]-j)*(n-sze[v]-(k-j)))*e[i].d);
}
}
}
for(int V=min(k,sze[u]);V>=1;V--) f[u][V]=max(f[u][V],f[u][V-1]);//到这里再分配u的颜色
}