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

《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 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.dtcms.com/a/348882.html

相关文章:

  • Spirng Cloud Alibaba主流组件
  • 【ElasticSearch】springboot整合es案例
  • 企业出海第一步:国际化和本地化
  • springBoot如何加载类(以atomikos框架中的事务类为例)
  • JavaScript数据结构详解
  • Docker知识点
  • 【数据分享】中国地势三级阶梯矢量数据
  • 【无标题】对六边形拓扑结构中的顶点关系、着色约束及量子隧穿机制进行严谨论述。
  • 深度剖析Spring AI源码(七):化繁为简,Spring Boot自动配置的实现之秘
  • MySQL--基础知识
  • 基础篇(下):神经网络与反向传播(程序员视角)
  • 多机多卡微调流程
  • Node.js依赖管理与install及run命令详解
  • 【文献阅读】生态恢复项目对生态系统稳定性的影响
  • CI/CD持续集成及持续交付详解
  • Jwt令牌设置介绍
  • 关于熵减 - 电子圆柱
  • feat(compliance): 添加电子商务法技术解读
  • PCB电路设计学习4 PCB图布局 PCB图布线
  • Python - 100天从新手到大师:第十五天函数应用实战
  • HTTP 接口调用工具类(OkHttp 版)
  • 如何用单张gpu跑sglang的数据并行
  • Java全栈开发面试实战:从基础到高并发场景的深度解析
  • MATLAB 与 Python 数据交互:数据导入、导出及联合分析技巧
  • `free` 内存释放函数
  • 【蓝桥杯 2024 省 C】挖矿
  • K8s 实战:六大核心控制器
  • yggjs_rlayout框架v0.1.2使用教程 01快速开始
  • python---类
  • 服装生产跟单系统是什么?主要功能有哪些?