NC19996 [HAOI2015]树上染色
题目
题目描述
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。
将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少。
输入描述
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。N ≤ 2000,0 ≤ K ≤ N
输出描述
输出一个正整数,表示收益的最大值。
示例1
输入
5 2
1 2 3
1 5 1
2 3 1
2 4 2
输出
17
说明
【样例解释】
将点1,2染黑就能获得最大收益。
备注
对于100% 的数据,\(0 \leq n,k \leq 2000\) 。
题解
知识点:树形dp,背包dp。
这道题长见识了,虽然显然是树上背包,但状态很妙。
设 \(dp[u][i]\) 为以 \(u\) 为根的子树选了 \(i\) 个点染黑后对答案的最大总贡献,注意这里是子树对答案的贡献,而非子树内的贡献。如果只是子树内的贡献,会发现转移时不知道点的具体位置,从而不能计算权值的变化,无法转移;而计算子树对整个答案的贡献就不需要考虑内点位置,而只要考虑父节点与子树根节点连的那条边的权值与子树染黑节点的数量即可转移。转移方程为:
\]
其中, \(val = j(m - j) \cdot w + (sz[v] - j)(n - m - (sz[v] - j)) \cdot w\) ,这里我把题目中的 \(K\) 改为了 \(m\) 方便使用。
首先转移方程表示为 \(u\) 为根的子树选了 \(i-j\) 个点染黑,\(v\) 为根的子树选了 \(j\) 个点染黑,并连接 \((u,v)\) 这条边产生 \(val\) 的总贡献是否更大。前面两个没什么问题,考虑 \(val\) 如何计算。
设 \((u,v)\) 的权为 \(w\) ,则 \((u,v)\) 两边的黑节点和白节点的每个组合都能多一次 \(w\) 的贡献。因此黑节点的组合贡献多了 \(j(m-j) \cdot w\) ,因为一端是子树 \(j\) 个黑节点,另一端是其他 \(m-j\) 个黑节点;白节点的组合贡献多了 \((sz[v] - j)(n - m - (sz[v] - j)) \cdot w\) ,因为子树有 \(sz[v]-j\) 个白节点,其他有 \(n-m-(sz[v]-j)\) 个白节点。最后加起来就是 \(val\) 。
到这里整道题算是做完了,但细节上有很多值得注意的。比如 \(j=0\) 时,会发现转移方程变为:
\]
直接原地更新了,这意味着更新 \(j=0\) 时, \(dp[u][i]\) 必须是原来的,这导致 \(j=0\) 必须第一个更新,才能更新别的,正序更新是直接满足这个要求。
除此之外还需要对dp范围进行剪枝,不然铁定超时。这里推荐用刷表法,因为最佳循环条件十分容易就能写出来(即没有浪费一点时间在没用的状态上),打表法不是不能写但非常麻烦,如下面我给出代码就是用打表法写的,虽然这道题用不着这么严格。
剪枝后的复杂度可以证明是 \(O(nm)\) 。
时间复杂度 \(O(nm)\)
空间复杂度 \(O(nm)\)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n, m;
vector<pair<int, int>> g[2007];
int sz[2007];
ll dp[2007][2007];
void dfs(int u, int fa) {
sz[u] = 1;
//dp[u][0] = dp[u][1] = 0;
for (auto [v, w] : g[u]) {
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
for (int i = min(sz[u], m);i >= 0;i--) {
for (int j = max(sz[v] - sz[u] + i, 0);j <= min(i, sz[v]);j++) {
///严格区间可以省掉许多时间(这道题是几百倍),但一般不敢这么严格,通常负无穷区间即可。
///不过这道题用刷表法后,严格区间会很好得到
///这道题需要加两个min限制一下不然会超时,但j的起点max是不必要的。
ll val = 1LL * j * (m - j) * w + 1LL * (sz[v] - j) * (n - m - (sz[v] - j)) * w;
dp[u][i] = max(dp[u][i], dp[u][i - j] + dp[v][j] + val);
///注意j=0时,dp[i][j]会被自己更新,如果j是倒序,会导致之前修改的重复作用,因此j=0必须第一个修改好,其他的顺序随意
}
}
}
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1;i < n;i++) {
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({ v,w });
g[v].push_back({ u,w });
}
//memset(dp, -0x3f, sizeof(dp));
dfs(1, 0);
cout << dp[1][m] << '\n';
return 0;
}
NC19996 [HAOI2015]树上染色的更多相关文章
- bzoj 4033: [HAOI2015]树上染色 [树形DP]
4033: [HAOI2015]树上染色 我写的可是\(O(n^2)\)的树形背包! 注意j倒着枚举,而k要正着枚举,因为k可能从0开始,会使用自己更新一次 #include <iostream ...
- BZOJ4033: [HAOI2015]树上染色(树形DP)
4033: [HAOI2015]树上染色 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 3461 Solved: 1473[Submit][Stat ...
- BZOJ4033 HAOI2015 树上染色 【树上背包】
BZOJ4033 HAOI2015 树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白 ...
- [BZOJ4033][HAOI2015]树上染色(树形DP)
4033: [HAOI2015]树上染色 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2437 Solved: 1034[Submit][Stat ...
- 【BZOJ4033】[HAOI2015]树上染色 树形DP
[BZOJ4033][HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染 ...
- BZOJ_4033_[HAOI2015]树上染色_树形DP
BZOJ_4033_[HAOI2015]树上染色_树形DP Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的 ...
- BZOJ 4033[HAOI2015] 树上染色(树形DP)
4033: [HAOI2015]树上染色 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 3188 Solved: 1366[Submit][Stat ...
- [HAOI2015]树上染色(树形dp)
[HAOI2015]树上染色 题目描述 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 . 将所 ...
- [HAOI2015]树上染色(树上dp)
[HAOI2015]树上染色 这种要算点对之间路径的长度和的题,难以统计每个点的贡献.这个时候一般考虑算每一条边贡献了哪些点对. 知道这个套路以后,那么这题就很好做了. 状态:设\(dp[u][i]\ ...
- [HAOI2015]树上染色 树状背包 dp
#4033. [HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白 ...
随机推荐
- python 基础 | 实现微秒级计时
搬运一个计时代码: import datetime s = datetime.datetime.now() # 开始 # do something e = datetime.datetime.now( ...
- 【SHELL】变量内容替换
使用场景:变量存放的路径通常在使用时需要进行斜杠转义 WORKSPACE=`pwd` WORKSPACE=$(echo ${WORKSPACE//\//\\/})
- 【ThreadX】Azure RTOS ThreadX概述
Azure RTOS ThreadX是Microsoft的高级工业级实时操作系统(RTOS),专门用于深度嵌入式,实时和IoT应用程序.Azure RTOS ThreadX提供了高级计划,通信,同步, ...
- Vue-自定义icon实现
在项目中引入了element-ui之后,发现其内置的icon有限,无法满足项目的需求,因此需自定义icon来实现需求. 在vue项目的components下新建SvgIcon目录,在SvgIcon目录 ...
- [转帖]TiFlash 面向编译器的自动向量化加速
作者:朱一帆 目录 SIMD 介绍 SIMD 函数派发方案 面向编译器的优化 SIMD 介绍 SIMD 是重要的重要的程序加速手段.CMU DB 组在 Advanced Database Syst ...
- [转帖]Tiup 常用运维操作命令干货
https://zhuanlan.zhihu.com/p/356031031 **导读**> 作者:杨漆> 16年关系型数据库管理,从oracle 9i .10g.11g.12c到Mysq ...
- [转帖]Jmeter跨线程组传参
https://www.cnblogs.com/a00ium/p/10462576.html 我们知道,同一线程组中可以通过"正则表达式提取器"获取其中一个取样器的响应结果中的 ...
- [转帖]淫技巧 | 如何查看已连接的wifi密码
https://blog.csdn.net/DynmicResource/article/details/120134984?spm=1001.2014.3001.5502 主题使用方法:https: ...
- 【转帖】Lua,LuaJIT,Luarocks的安装与配置-史上最详细【Linux】
目录 一,lunux下lua安装 二,安装luarocks---lua包管理工具 三,LuaJIT的安装 既然各位都点开看了,那么Lua语言不用我介绍了吧,LuaJIT是lua的一个Just-In-T ...
- [转帖]JVM监控及诊断工具-命令行
https://www.cnblogs.com/xiaojiesir/p/15622372.html 性能指标 停顿时间(响应时间) 提交请求和返回响应之间使用的时间,一般比较关注平均响应时间 常用操 ...