Luogu P3177 树上染色 [ 蓝 ] [ 树形 dp ] [ 贡献思维 ]
一道很好的树形 dp !!!!!
树上染色。
错误思路
定义
但这样写,就在转移的时候必须枚举每一个点,复杂度过大,而且还不好写,是十分错误的写法。
正确思路
一般看到有关树上“路径”的题,就要把路径拆成一个个独立的单边,对每个单边独立计算贡献。
我们尝试对某一条边
设儿子下面有
那么经过这条边的路径总数为:儿子下面和父亲上面的黑点的路径数
然后乘上这条边的长度
就是这条边的贡献。
于是 dp 状态就出来了:
定义
对于转移,我们只需要考虑
细节处理
上界的处理(for(int i=min(k,sz[u]);i>=0;i--)
与for(int j=0;j<=min(i,sz[v]);j++)
)不必多说,这题的坑点在于下界:
考虑一条链的数据,对于一个点
所以如果我们不控制循环的下界,那么在链的 hack 下复杂度将直逼
这是因为除去该子树,自己父节点下的其他子树的总结点数比
所以解一个不等式:
解得
这就是
时间复杂度
树形背包检验自己复杂度正确与否,只需要构造一条链的数据,看看会不会被卡成立方的就好了。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k,sz[2005];
struct edge{
int to;
ll w;
};
vector<edge>s[2005];
ll dp[2005][2005];
void dfs(int u,int fa)
{
sz[u]=1;
for(auto tmp:s[u])
{
int v=tmp.to;
ll w=tmp.w;
if(v==fa)continue;
dfs(v,u);
sz[u]+=sz[v];
for(int i=min(k,sz[u]);i>=0;i--)
{
for(int j=max(0,i-sz[u]+sz[v]);j<=min(i,sz[v]);j++)
{
dp[u][i]=max(dp[u][i],dp[v][j]+dp[u][i-j]+(1ll*j*(k-j)+1ll*(sz[v]-j)*(n-k-(sz[v]-j)))*w);
}
}
}
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n-1;i++)
{
int u,v;
ll w;
cin>>u>>v>>w;
s[u].push_back({v,w});
s[v].push_back({u,w});
}
dfs(1,0);
cout<<dp[1][k];
return 0;
}
标签:
动态规划 dp
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战