题目描述

给定一棵有 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 degree[MAX_NODE];          // 每个节点的度数
int weight[MAX_NODE];          // 每个节点的权重
LONG_LONG dp[MAX_NODE][2];     // 动态规划数组,dp[u][0/1]表示节点u的两种状态
vector<int> graph[MAX_NODE];   // 图的邻接表表示
vector<int> parent_choices[MAX_NODE][2];  // 记录每个状态下的父节点选择

// 深度优先搜索,计算dp值
void dfs(int current_node, int parent_node) {
    // 如果是叶子节点(度数为1且有父节点)
    if (degree[current_node] == 1 && parent_node != 0) {
        dp[current_node][1] = weight[current_node];  // 选择当前节点
        dp[current_node][0] = 0;                     // 不选择当前节点
        return;
    }
    
    LONG_LONG sum = 0;
    // 计算所有子节点选择状态下的和
    for (int i = 0; i < graph[current_node].size(); ++i) {
        int child_node = graph[current_node][i];
        if (child_node != parent_node) {
            dfs(child_node, current_node);
            sum += dp[child_node][1];
        }
    }
    
    // 初始化当前节点的两种状态
    dp[current_node][1] = sum;
    dp[current_node][0] = sum;
    
    // 尝试更新当前节点的两种状态
    for (int i = 0; i < graph[current_node].size(); ++i) {
        int child_node = graph[current_node][i];
        if (child_node != parent_node) {
            // 更新选择当前节点的状态
            dp[current_node][1] = min(dp[current_node][1], sum - dp[child_node][1] + dp[child_node][0] + weight[current_node]);
            // 更新不选择当前节点的状态
            dp[current_node][0] = min(dp[current_node][0], sum - dp[child_node][1] + dp[child_node][0]);
        }
    }
    
    // 记录每个状态下的父节点选择
    for (int i = 0; i < graph[current_node].size(); ++i) {
        int child_node = graph[current_node][i];
        if (child_node != parent_node) {
            if (dp[current_node][1] == sum - dp[child_node][1] + dp[child_node][0] + weight[current_node]) {
                parent_choices[current_node][1].push_back(child_node);
            }
            if (dp[current_node][0] == sum - dp[child_node][1] + dp[child_node][0]) {
                parent_choices[current_node][0].push_back(child_node);
            }
        }
    }
}

bool selected[MAX_NODE];       // 标记节点是否被选中
bool visited[MAX_NODE][2];     // 标记节点的状态是否已访问

// 节点结构体,用于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 (degree[current_node] == 1 && parent_node != 0) {
            if (current_state) {
                selected[current_node] = true;
            }
            continue;
        }
        
        // 重新计算子节点选择状态的和
        LONG_LONG sum = 0;
        for (int i = 0; i < graph[current_node].size(); ++i) {
            int child_node = graph[current_node][i];
            if (child_node != parent_node) {
                sum += dp[child_node][1];
            }
        }
        
        // 处理特殊情况
        if (sum == dp[current_node][current_state]) {
            for (int i = 0; i < graph[current_node].size(); ++i) {
                int child_node = graph[current_node][i];
                if (child_node != parent_node && !visited[child_node][1]) {
                    visited[child_node][1] = true;
                    q.push({child_node, current_node, 1});
                }
            }
        }
        
        // 根据父节点选择数量处理不同情况
        if (parent_choices[current_node][current_state].size() == 1) {
            // 只有一个选择的情况
            if (current_state || weight[current_node] == 0) {
                selected[current_node] = true;
            }
            
            int chosen_child = parent_choices[current_node][current_state][0];
            for (int i = 0; i < graph[current_node].size(); ++i) {
                int child_node = graph[current_node][i];
                if (child_node != parent_node && child_node != chosen_child && !visited[child_node][1]) {
                    visited[child_node][1] = true;
                    q.push({child_node, current_node, 1});
                }
            }
            
            if (!visited[chosen_child][0]) {
                visited[chosen_child][0] = true;
                q.push({chosen_child, current_node, 0});
            }
        } else if (parent_choices[current_node][current_state].size() > 1) {
            // 多个选择的情况
            if (current_state || weight[current_node] == 0) {
                selected[current_node] = true;
            }
            
            for (int i = 0; i < graph[current_node].size(); ++i) {
                int child_node = graph[current_node][i];
                if (child_node != parent_node && !visited[child_node][1]) {
                    q.push({child_node, current_node, 1});
                }
            }
            
            for (int i = 0; i < parent_choices[current_node][current_state].size(); ++i) {
                int child_node = parent_choices[current_node][current_state][i];
                if (!visited[child_node][0]) {
                    visited[child_node][0] = true;
                    q.push({child_node, current_node, 0});
                }
            }
        }
    }
}

int main() {
    scanf("%d", &node_count);
    
    // 读取节点权重
    for (int i = 1; i <= node_count; ++i) {
        scanf("%d", &weight[i]);
    }
    
    // 读取边信息,构建图
    for (int i = 1; i < node_count; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        graph[x].push_back(y);
        graph[y].push_back(x);
        degree[x]++;
        degree[y]++;
    }
    
    // 计算dp值
    dfs(1, 0);
    
    // 确定选择的节点
    bfs();
    
    // 统计并输出结果
    int selected_count = 0;
    for (int i = 1; i <= node_count; ++i) {
        if (selected[i]) {
            selected_count++;
        }
    }
    
    printf("%lld %d\n", dp[1][1], selected_count);
    for (int i = 1; i <= node_count; ++i) {
        if (selected[i]) {
            printf("%d ", i);
        }
    }
    
    return 0;
}
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94439.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94439.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94439.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CPTS-Agile (Werkzeug / Flask Debug)

枚举 nmap -sC -sV -T4 -Pn -n -p- 10.10.11.203进行常规的网页枚举和测试发现报错信息&#xff0c;‘Werkzeug / Flask Debug’ 测试Export导出功能发现存在路径遍历查看这篇文章 https://book.hacktricks.wiki/zh/network-services-pentesting/pentesting-web/werkzeug.html#…

【网络运维】Shell 脚本编程:while 循环与 until 循环

Shell 脚本编程&#xff1a;while 循环与 until 循环 循环结构简介 循环语句是 Shell 脚本中用于重复执行一条或一组指令的重要工具&#xff0c;直到满足特定条件时停止执行。Shell 脚本中常见的循环语句包括 while、until、for 和 select。本文将重点介绍 while 和 until 两种…

LLM 中评价指标与训练概要介绍

在【LLM】LLM 中增量解码与模型推理解读一文中对 LLM 常见名词进行了介绍&#xff0c;本文会对 LLM 中评价指标与训练概要进行介绍&#xff0c;本文并未介绍训练实操细节&#xff0c;未来有机会再了解&#xff5e; 一、LLM 如何停止输出 在看 LLM 评价指标前&#xff0c;先看…

Java 20 新特性及具体应用

目录 1. 模式匹配 for switch&#xff08;预览特性&#xff09; 2. 记录模式&#xff08;预览特性&#xff09; 3. 外部函数与内存 API&#xff08;预览特性&#xff09; 4. 矢量 API&#xff08;孵化器特性&#xff09; 5. 作用域值&#xff08;预览特性&#xff09; 6. …

【STM32】CubeMX(十一):FreeRTOS任务挂起与解挂

这篇文章是 STM32 HAL FreeRTOS 下的任务挂起与恢复机制&#xff0c; 结合 CubeMX 图示与代码&#xff0c;构建了一个 FreeRTOS 控制示例。 本篇目标&#xff1a;创建两个任务&#xff1a; 一个控制蓝灯闪烁&#xff08;myTask01&#xff09; 另一个监控按键&#xff08;Start…

图片预加载:提升Web性能的关键

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

大模型压缩三剑客:量化、剪枝与知识蒸馏全解析

在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;LLM&#xff09;如通义千问、GPT 等已成为推动智能应用的核心引擎。然而&#xff0c;这些模型动辄数十亿甚至上千亿参数&#xff0c;带来了高昂的计算成本和部署门槛。如何在不显著牺牲性能的前提下&#xff0c;让大…

Seaborn数据可视化实战:Seaborn基础图表绘制入门

基础图表绘制&#xff1a;Seaborn入门教程 学习目标 通过本课程的学习&#xff0c;你将掌握如何使用Seaborn库绘制基础图表&#xff0c;包括条形图、折线图和散点图。你将了解Seaborn的基本函数和参数设置&#xff0c;以及如何通过调整这些参数来优化图表的视觉效果。 相关知识…

阿里开源通义万相Wan2.2:视频生成技术的革命性突破

在人工智能视频生成领域,阿里云通义实验室于2025年7月重磅开源了新一代视频生成大模型 Wan2.2,其核心亮点包括人体动作生成的极致精度、电影级美学表达以及高效的资源利用效率,标志着视频生成技术迈入了一个全新的阶段。 一、核心功能:三大模型,覆盖全场景视频生成 Wan2.…

说说你对Integer缓存的理解?

大家好&#xff0c;我是锋哥。今天分享关于【说说你对Integer缓存的理解?】面试题。希望对大家有帮助&#xff1b; 说说你对Integer缓存的理解? 超硬核AI学习资料&#xff0c;现在永久免费了&#xff01; Integer 缓存是 Java 中一个优化机制&#xff0c;它主要通过缓存一部…

高速CANFD收发器ASM1042在割草机器人轮毂电机通信系统中的适配性研究

摘要割草机器人轮毂电机的通信系统对其实现自主控制和高效作业至关重要。本文旨在研究国科安芯推出的高速CANFD收发器芯片ASM1042是否能够满足割草机器人轮毂电机通信系统的复杂需求。通过详细分析轮毂电机通信系统的性能要求&#xff0c;以及ASM1042的电气、功能和环境特性&am…

MTK Linux DRM分析(十二)- KMS Panel框架层(drm_panel.c、drm_mipi_dbi.c、drm_mipi_dsi.c)

一、简介 三个代码文件(drm_mipi_dbi.c、drm_panel.c、drm_mipi_dsi.c)的分析。这些文件都是Linux内核DRM(Direct Rendering Manager)子系统的组成部分,主要用于支持显示面板,特别是通过MIPI(Mobile Industry Processor Interface)接口的显示设备。它们提供了显示驱动…

合合信息acge模型获C-MTEB第一,文本向量化迎来新突破

前言&#xff1a; 在当今时代&#xff0c;大型语言模型以其惊人的发展速度和广泛的应用前景&#xff0c;正成为全球科技界的瞩目焦点。这些模型的强大能力&#xff0c;源自于背后默默支撑它们的Embedding技术——一种将语言转化为机器可理解的数值向量的关键技术。随着大型语言…

26.内置构造函数

2.内置构造函数2.1Object2.2Array2.3String2.4number

tauri配置允许执行eval脚本,在打包cocos游戏web/phone移动端的时候一定要配置

解决办法&#xff1a;在tauriconfig中配置"csp": "default-src self asset: unsafe-inline customprotocol://* http://localhost:* ws:localhost:* unsafe-eval ipc: http://ipc.localhost; script-src unsafe-eval self https://www.googletagmanager.com uns…

K 均值聚类算法学习总结

一、聚类算法基础认知 核心概念&#xff1a;聚类属于无监督学习&#xff0c;核心是把 “相似的样本” 自动分到同一组&#xff08;簇&#xff09;&#xff0c;不需要预先标注的标签。主要挑战是怎么定义 “相似性”、评估聚类效果以及确定最好的聚类数量。 距离度量&#xff1a…

基于Spring Cloud Gateway动态路由与灰度发布方案对比与实践指导

基于Spring Cloud Gateway动态路由与灰度发布方案对比与实践指导 一、问题背景介绍 在微服务架构中&#xff0c;API网关负责统一入口、路由分发与权限校验功能。随着业务需求的不断演进&#xff0c;如何灵活地实现路由动态更新、版本灰度发布以及流量打点就成为运维和开发团队的…

MySQL InnoDB Buffer Pool详解:原理、配置与性能优化

1. 为什么需要 Buffer Pool&#xff1f;1.1 数据库性能瓶颈分析在 MySQL 的运行过程中&#xff0c;最核心的性能瓶颈来自磁盘 IO。磁盘访问延迟&#xff1a;一次机械硬盘 IO 操作可能需要数毫秒&#xff0c;即使是 SSD&#xff0c;访问延迟也在几十微秒量级。内存访问延迟&…

ArcGIS Pro 安装路径避坑指南:从崩溃根源到规范实操(附问题修复方案)

作为 GIS 从业者&#xff0c;你是否遇到过这些糟心场景&#xff1a;ArcGIS Pro 双击启动无响应、运行中突然弹出 “Runtime Error” 崩溃、加载矢量数据时提示 “找不到指定文件”&#xff1f;排查半天后发现&#xff0c;这些问题的 “元凶” 竟藏在安装路径里 —— 中文路径或…

Python 实战:内网渗透中的信息收集自动化脚本(2)

用途限制声明&#xff0c;本文仅用于网络安全技术研究、教育与知识分享。文中涉及的渗透测试方法与工具&#xff0c;严禁用于未经授权的网络攻击、数据窃取或任何违法活动。任何因不当使用本文内容导致的法律后果&#xff0c;作者及发布平台不承担任何责任。渗透测试涉及复杂技…