当前位置: 首页 > news >正文

洛谷P3177

题目大意

在一棵带权树上选择 \(k\) 个节点染成黑色,其余节点染成白色,使得所有黑点对距离之和加上所有白点对距离之和最大。

问题转化

暴力枚举的时间复杂度是 \(O(2^n)\),对于 \(n \leq 2000\) 不可接受。那么,这道题该如何入手呢?

关键:考虑每条边对总收益的贡献。

对于树中的一条边 \((u,v)\),假设边权为 \(w\)

  • \(u\) 一侧有 \(x\) 个黑点,\(sz[v] - x\) 个白点。
  • \(v\) 一侧有 \(k-x\) 个黑点,\((n-sz[v])-(k-x)\) 个白点。
    这条边被黑点对经过的次数为:\(x \times (k-x)\)
    这条边被白点对经过的次数为:\((sz[v]-x) \times (n-k-(sz[v]-x))\)
    因此,这条边的总贡献为:

\[w \times \left[ x \times (k - x) + (sz[v] - x) \times (n - k - sz[v] + x) \right] \]

Solution

标签:树形DP/树上背包

  1. 状态定义
    \(dp[u][j]\) 表示以 \(u\) 为根的子树中,选择 \(j\) 个黑点时,子树内能获得的最大收益。
  2. 状态转移
    对于节点 \(u\) 的每个子节点 \(v\),考虑在子树 \(v\) 中选择 \(t\) 个黑点(\(0 \leq t \leq \min(j, sz[v])\)):
  • 子树 \(v\) 内部已经贡献了 \(dp[v][t]\)
  • \((u,v)\) 的贡献为:

\[w \times [t \times (k-t) + (sz[v]-t) \times (n-k-sz[v]+t)] \]

  • \(u\) 的其他子树选择 \(j-t\) 个黑点,贡献为 \(dp[u][j-t]\)
    因此:

\[dp[u][j] = \max_{0 \leq t \leq \min(j, sz[v])} \{dp[u][j-t] + dp[v][t] + w \times [t \times (k-t) + (sz[v]-t) \times (n-k-sz[v]+t)]\} \]

时间复杂度

总时间复杂度:\(O(n^2)\)
对于 \(n \leq 2000\),可以接受。

参考代码

#include <bits/stdc++.h>
using namespace std;
#define N 2010
typedef long long ll;ll n, m;  // n: 节点数, m: 要选择的黑点数(k)
ll sz[N]; // sz[i]: 以i为根的子树大小
ll head[N], to[N << 1], nxt[N << 1], nw[N << 1], cnt; 
ll dp[N][N]; // dp[i][j]: 以i为根的子树中选择j个黑点的最大收益void dfs(ll u, ll f) {sz[u] = 1;                  // 自身大小dp[u][0] = dp[u][1] = 0;    // 初始化:选0个或1个黑点收益为0// 遍历所有子节点for (ll i = head[u]; i; i = nxt[i]) {ll v = to[i];if (v == f) continue;   dfs(v, u);              // 递归处理子节点sz[u] += sz[v];         // 更新子树大小// 合并子树v到u(类似01背包,倒序枚举)for (ll j = min(m, sz[u]); j >= 0; j--) {// 先考虑子树v全白的情况(t=0)if (dp[u][j] != -1) {// 边(u,v)的贡献:子树v中所有点都是白色dp[u][j] += dp[v][0] + sz[v] * (n - m - sz[v]) * nw[i];}// 考虑子树v中选择t个黑点(t>0)for (ll t = min(j, sz[v]); t > 0; t--) {if (dp[u][j - t] == -1) continue; // 状态不可达// 计算边(u,v)的贡献ll edge_contrib = nw[i] * (t * (m - t) + (sz[v] - t) * (n - m - sz[v] + t));// 更新dp[u][j]dp[u][j] = max(dp[u][j], dp[u][j - t] + dp[v][t] + edge_contrib);}}}
}int main() {cin >> n >> m;for (ll i = 1; i < n; i++) {ll u, v, w;cin >> u >> v >> w;to[++cnt] = v;nxt[cnt] = head[u];head[u] = cnt;nw[cnt] = w;to[++cnt] = u;nxt[cnt] = head[v];head[v] = cnt;nw[cnt] = w;}// 初始化dp数组为-1(表示不可达状态)memset(dp, -1, sizeof(dp));dfs(1, 0);// 输出以1为根的树中选择m个黑点的最大收益cout << dp[1][m];return 0;
}
http://www.jsqmd.com/news/359254/

相关文章:

  • 洛谷P1896
  • 计算机小程序毕设实战-基于springboot+小程序的桂林旅游桂林源记小程序的设计与实现基于springboot桂林旅游景点导游平台【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 论文写作利器:6款AI工具打造专业级内容
  • Leetcode 剑指 Offer II 161. 连续天数的最高销售额
  • 【毕业设计】基于springboot+小程序的桂林旅游桂林源记小程序的设计与实现(源码+文档+远程调试,全bao定制等)
  • 别被名字吓到:锯齿迭代器(Zigzag Iterator)其实是个“很人性”的算法
  • 智能辅助:6款AI工具优化论文写作流程与成果
  • 小程序毕设选题推荐:基于springboot+小程序的医院挂号系统小程序基于SpringBoot智能在线预约挂号系统微信小程序【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 小程序计算机毕设之基于springboot+小程序的医院挂号系统小程序基于SpringBoot+微信小程序的微信医院挂号系统(完整前后端代码+说明文档+LW,调试定制等)
  • 适合小白的git的基础使用方法
  • 借助AI技术:6款工具让论文写作更轻松精准
  • 【计算机毕业设计案例】基于springboot的文化旅游小程序基于springboot+小程序的桂林旅游桂林源记小程序的设计与实现(程序+文档+讲解+定制)
  • 【计算机毕业设计案例】基于springboot+小程序的桂林旅游桂林源记小程序桂林旅游景点导游平台的设计与实现(程序+文档+讲解+定制)
  • 提升学术写作:6款AI工具助你高效完成论文
  • 【计算机毕业设计案例】基于JAVA+SpringBoot+MySQL+微信小程序的校园点餐系统基于springboot+小程序的校园点餐系统小程序的设计与实现(程序+文档+讲解+定制)
  • 论文写作新选择:6款AI工具实现高效与高质量
  • 洛谷P4035
  • 第一章使用Navicat创建数据库
  • 小程序毕设项目:基于springboot+小程序的桂林旅游桂林源记小程序的设计与实现(源码+文档,讲解、调试运行,定制等)
  • 洛谷P2607
  • 我们去看看小明和小华交流什么神秘的C++项目吧
  • Visual Studio 2026新解决方案格式slnx详解
  • 你永远不知道用户会怎样使用你的软件
  • CF2155E
  • 【CTFshow-pwn系列】03_栈溢出【pwn 042】详解:64位下的字符串搜寻与 ROP
  • 安全工具篇动态绕过DumpLsass凭据SysCALL调用对抗EDR打乱源头特征
  • 网络编程 SDN
  • 小程序计算机毕设之基于springboot+小程序的校园点餐系统小程序的设计与实现基于JAVA+SpringBoot+MySQL+微信小程序的校园点餐系统(完整前后端代码+说明文档+LW,调试定制等)
  • 以工程思维,破局软件开发的混沌
  • 详细介绍:C++起始之路——类和对象(下)