《CF1120D Power Tree》

题目描述

给定一棵有 n 个顶点的有根树,树的根为顶点 1。每个顶点都有一个非负的价格。树的叶子是指度为 1 且不是根的顶点。

Arkady 和 Vasily 在树上玩一个奇怪的游戏。游戏分为三个阶段。第一阶段,Arkady 购买树上的一些非空顶点集合。第二阶段,Vasily 给所有叶子节点赋一些整数。第三阶段,Arkady 可以进行若干次(也可以不进行)如下操作:选择他在第一阶段购买的某个顶点 v 和某个整数 x,然后将 x 加到 v 的子树中所有叶子的整数上。整数 x 可以是正数、负数或零。

如果一条从叶子 a 到根的简单路径经过顶点 b,则称叶子 a 在顶点 b 的子树中。

Arkady 的目标是让所有叶子上的整数都变为零。无论 Vasily 在第二阶段如何赋值,Arkady 都要保证自己能够达成目标。请你求出 Arkady 在第一阶段至少需要支付的总费用 s,以及有多少个顶点属于至少一个最优集合(即总费用为 s 的集合),使得 Arkady 购买这些顶点后能够保证胜利。

请你输出所有属于至少一个最优集合的顶点编号。

输入格式

第一行包含一个整数 n(2≤n≤200000),表示树的顶点数。

第二行包含 n 个整数 c1​,c2​,...,cn​(0≤ci​≤109),其中 ci​ 表示第 i 个顶点的价格。

接下来的 n−1 行,每行包含两个整数 a 和 b(1≤a,b≤n),表示树上的一条边。

输出格式

第一行输出两个整数:Arkady 至少需要支付的最小总费用 s,以及属于至少一个最优集合的顶点个数 k。

第二行输出 k 个不同的整数,按升序排列,表示属于至少一个最优集合的顶点编号。

显示翻译

题意翻译

输入输出样例

输入 #1复制

复制代码
5
5 1 3 2 1
1 2
2 3
2 4
1 5

输出 #1复制

复制代码
4 3
2 4 5 

输入 #2复制

复制代码
3
1 1 1
1 2
1 3

输出 #2复制

复制代码
2 3
1 2 3 

说明/提示

在第二个样例中,所有包含两个顶点的集合都是最优的。因此,每个顶点都至少属于一个最优集合。

由 ChatGPT 4.1 翻译

代码实现:

#include <bits/stdc++.h>

using namespace std;

#define LONG_LONG long long

const int MAX_NODE = 2e5 + 5;

int node_count; // 节点总数

int degreeMAX_NODE; // 每个节点的度数

int weightMAX_NODE; // 每个节点的权重

LONG_LONG dpMAX_NODE2; // 动态规划数组,dpu0/1表示节点u的两种状态

vector<int> graphMAX_NODE; // 图的邻接表表示

vector<int> parent_choicesMAX_NODE2; // 记录每个状态下的父节点选择

// 深度优先搜索,计算dp值

void dfs(int current_node, int parent_node) {

// 如果是叶子节点(度数为1且有父节点)

if (degreecurrent_node == 1 && parent_node != 0) {

dpcurrent_node1 = weightcurrent_node; // 选择当前节点

dpcurrent_node0 = 0; // 不选择当前节点

return;

}

LONG_LONG sum = 0;

// 计算所有子节点选择状态下的和

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node) {

dfs(child_node, current_node);

sum += dpchild_node1;

}

}

// 初始化当前节点的两种状态

dpcurrent_node1 = sum;

dpcurrent_node0 = sum;

// 尝试更新当前节点的两种状态

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node) {

// 更新选择当前节点的状态

dpcurrent_node1 = min(dpcurrent_node1, sum - dpchild_node1 + dpchild_node0 + weightcurrent_node);

// 更新不选择当前节点的状态

dpcurrent_node0 = min(dpcurrent_node0, sum - dpchild_node1 + dpchild_node0);

}

}

// 记录每个状态下的父节点选择

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node) {

if (dpcurrent_node1 == sum - dpchild_node1 + dpchild_node0 + weightcurrent_node) {

parent_choicescurrent_node1.push_back(child_node);

}

if (dpcurrent_node0 == sum - dpchild_node1 + dpchild_node0) {

parent_choicescurrent_node0.push_back(child_node);

}

}

}

}

bool selectedMAX_NODE; // 标记节点是否被选中

bool visitedMAX_NODE2; // 标记节点的状态是否已访问

// 节点结构体,用于BFS

struct Node {

int node; // 当前节点

int parent; // 父节点

int state; // 状态(0或1)

};

queue<Node> q; // BFS队列

// 广度优先搜索,确定最终选择的节点

void bfs() {

q.push({1, 0, 1}); // 从根节点(1号节点)开始,状态为1

while (!q.empty()) {

Node current = q.front();

q.pop();

int current_node = current.node;

int parent_node = current.parent;

int current_state = current.state;

// 如果是叶子节点

if (degreecurrent_node == 1 && parent_node != 0) {

if (current_state) {

selectedcurrent_node = true;

}

continue;

}

// 重新计算子节点选择状态的和

LONG_LONG sum = 0;

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node) {

sum += dpchild_node1;

}

}

// 处理特殊情况

if (sum == dpcurrent_nodecurrent_state) {

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node && !visitedchild_node1) {

visitedchild_node1 = true;

q.push({child_node, current_node, 1});

}

}

}

// 根据父节点选择数量处理不同情况

if (parent_choicescurrent_nodecurrent_state.size() == 1) {

// 只有一个选择的情况

if (current_state || weightcurrent_node == 0) {

selectedcurrent_node = true;

}

int chosen_child = parent_choicescurrent_nodecurrent_state0;

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node && child_node != chosen_child && !visitedchild_node1) {

visitedchild_node1 = true;

q.push({child_node, current_node, 1});

}

}

if (!visitedchosen_child0) {

visitedchosen_child0 = true;

q.push({chosen_child, current_node, 0});

}

} else if (parent_choicescurrent_nodecurrent_state.size() > 1) {

// 多个选择的情况

if (current_state || weightcurrent_node == 0) {

selectedcurrent_node = true;

}

for (int i = 0; i < graphcurrent_node.size(); ++i) {

int child_node = graphcurrent_nodei;

if (child_node != parent_node && !visitedchild_node1) {

q.push({child_node, current_node, 1});

}

}

for (int i = 0; i < parent_choicescurrent_nodecurrent_state.size(); ++i) {

int child_node = parent_choicescurrent_nodecurrent_statei;

if (!visitedchild_node0) {

visitedchild_node0 = true;

q.push({child_node, current_node, 0});

}

}

}

}

}

int main() {

scanf("%d", &node_count);

// 读取节点权重

for (int i = 1; i <= node_count; ++i) {

scanf("%d", &weighti);

}

// 读取边信息,构建图

for (int i = 1; i < node_count; ++i) {

int x, y;

scanf("%d%d", &x, &y);

graphx.push_back(y);

graphy.push_back(x);

degreex++;

degreey++;

}

// 计算dp值

dfs(1, 0);

// 确定选择的节点

bfs();

// 统计并输出结果

int selected_count = 0;

for (int i = 1; i <= node_count; ++i) {

if (selectedi) {

selected_count++;

}

}

printf("%lld %d\n", dp11, selected_count);

for (int i = 1; i <= node_count; ++i) {

if (selectedi) {

printf("%d ", i);

}

}

return 0;

}

相关推荐
2601_961875241 天前
法考资料2026|全套|资料已整理
数据结构·算法·链表·贪心算法·eclipse·线性回归·动态规划
汉克老师1 天前
GESP2026年3月认证C++六级真题与解析(编程题1 选数)
c++·动态规划·线性dp·gesp六级·状态转移·选与不选
渡之1 天前
GRiM-Net 深度解析 | 无人机 GNSS 拒止场景下两阶段跨视角视觉定位框架
深度学习·算法·动态规划·无人机
xhtdj1 天前
Uber 如何通过批处理实现单账户每秒30+次更新
大数据·数据库·人工智能·安全·动态规划
wabs6661 天前
关于动态规划【纯粹的0-1背包需要思考的问题】
算法·动态规划
hai3152475431 天前
九章编程法 · 字典引擎【0/1拓扑步进 · 矩阵压缩·终极封版】
人工智能·数学建模·性能优化·动态规划·代码复审·傅立叶分析·极限编程
-森屿安年-1 天前
62. 不同路径
算法·动态规划
-森屿安年-2 天前
91. 解码方法
c++·动态规划
wabs6662 天前
关于动态规划【力扣96.不同的二叉搜索树的递推公式怎么理解?】
算法·动态规划
珊瑚里的鱼2 天前
【动态规划】买卖股票的最佳时机Ⅲ
算法·动态规划