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

较难的换根dp:P6213 「SWTR-04」Collecting Coins

传送门

前题提要:感觉这道换根dp可以说是集中了换根dp的所有较高难度的操作和思想,以及较高的一些实现细节,可以说能够完全写出这道题才叫真正理解了换根dp,非常值得一做.

首先读完题意,不难发现这道题有很多限制.点的访问次数限制,必须访问某一个点,想要获得最大的贡献,没有限制初始起点.乍一看感觉完全没办法解决,我们考虑一个一个的去解决上述限制.

首先假设只想获得最大的贡献,那么这个显然是一个简单的树形dp,也就是 f [ u ] = m a x ( f [ v ] + w ) f[u]=max(f[v]+w) f[u]=max(f[v]+w). f [ u ] f[u] f[u]表示访问 u u u子树所能获得的最大贡献.
然后我们考虑加上访问次数的限制,我们发现对于一个点,无论他是起始点还是中途经过的点,只要访问这个点,我们就损失了一个进入机会.然后显然我们想访问一个子树获得子树的贡献就需要花费一次进入机会,因为我们进入一个子树必然需要返回.诶,此时我们就可以解决了.假设一个点的访问次数限制为 k k k,那么对于这个点来说,我们最多只能加入 k − 1 k-1 k1个子树的贡献.所以我们考虑对所有子树的贡献进行一个排序,然后取前 k − 1 k-1 k1个最大的贡献即可.
然后我们再考虑加入必须访问一个点的贡献.这一点直接解决起来就有点困难了,我们需要转化一下这个条件.我们考虑将这个必须访问的点当做我们的遍历的树的根节点,这样我们的必须访问的需求就迎刃而解了.因为对于刚开始的一棵树来说,我们这个根节点是必须遍历的,没有任何问题.
然后因为本题并没有限制初始点,所以我们需要进行一个换根.那么对于换完根的树来说,因为存在一个必须访问的点,所以我们限制换完根的子树必须选祖先那个子树即可.然后在自己的子树里选出前 k − 2 k-2 k2大的.


至此本题的主要解决思路就结束了,但是本题在换根上的细节和经验问题还是比较多的(反正我当时在写这道题被细节方面搞了很久),接下来详细讲讲细节方面的处理

其实建议不看下文自己解决细节问题,毕竟只有自己的连续思考才毕竟清楚

考虑我们维护出了 f [ u ] f[u] f[u]表示访问一个节点所能获得的最大贡献,并且因为换根需求,我们考虑记录使用 m a x _ f max\_f max_f记录下所有的u子树对u的贡献,并且对此从大到小排个序.
PS:这种记录子树对u的贡献的方法在很多题目中都需要体现(因为换根的时候需要考虑自身节点对父亲的贡献),但是很多题目都是记录最大值和次大值即可,这样较为方便,但是实际上那些题目都是可以直接记录所有的贡献然后等会处理,并且在本题中光记录最大和次大并不能解决问题,所以考虑直接记录所有的贡献

因为我们的父亲节点是必选的,为了方便解决问题,不妨设置一个辅助数组 f a _ v a l [ u ] fa\_val[u] fa_val[u]来记录以 u u u为根时 u u u节点的父亲子树对 u u u的贡献.然后设 d p [ u ] dp[u] dp[u]记录以 u u u为根时的贡献.
先来维护 f a _ v a l [ u ] fa\_val[u] fa_val[u]数组
然后我们考虑根节点从 u − > v u->v u>v转移,此时需要进行分类讨论,考虑先维护辅助数组:

  1. 当v子树是 f [ u ] f[u] f[u]数组的一个贡献来源时:此时我们想要维护 f a _ v a l [ u ] fa\_val[u] fa_val[u]时,我们需要花费一次进入机会从 v v v进入到 u u u,然后我们还需要一次进入机会从 u u u u u u的父亲节点(因为父亲节点是必选的),所以此时我们就只剩下 k − 2 k-2 k2棵可选择的子树贡献(并且这个k-2可子树是不能包括v的),那么考虑预处理出原本 u u u子树原 k − 1 k-1 k1课子树的总贡献,然后减掉 v v v这个子树的贡献就维护出了前 k − 2 k-2 k2棵除v子树外的最大贡献了.但是光光这样的方式是有问题的(当时卡了我很长时间),因为假设我们的u是根节点时,我们此时没有父亲那边的贡献,并且我们也不需要花一次进入机会去进入到父亲节点,所以这种情况下是需要特判的,具体特判方式看下文代码即可,此处不在赘述
  2. 考虑v子树不是 f [ u ] f[u] f[u]的一个贡献来源,那么此时我们想要维护 f a _ v a l [ u ] fa\_val[u] fa_val[u]时,我们同样此时只需要记录 u u u子树除 v v v外的前 k − 2 k-2 k2棵子树的贡献即可.因为此时v不处于前 k − 1 k-1 k1大的贡献之间,所以我们直接预处理前 k − 2 k-2 k2 m a x _ f max\_f max_f的和即可.同样的,当我们是处于根节点时照样需要特判.

考虑我们如果维护出了 f a _ v a l fa\_val fa_val数组之后,对于我们的 d p dp dp数组就不难维护了,我们此时的 d p [ u ] = f a _ v a l [ u ] + u 前 k − 2 大的子树的贡献和 dp[u]=fa\_val[u]+u前k-2大的子树的贡献和 dp[u]=fa_val[u]+uk2大的子树的贡献和

PS:此处同时也有一个小trick,因为时间复杂度的约束,我们不能实时的枚举v是否是u的一个贡献子树,所以我们需要在遍历子树之前先预处理一下这个状态,并且同时记录下前k-1大,前k大,前k-2大的子树的贡献和即可


下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {ll x=0,w=1;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x*w;
}
inline void print(__int128 x){if(x<0) {putchar('-');x=-x;}if(x>9) print(x/10);putchar(x%10+'0');
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Edge{int v,w;
};
vector<Edge>edge[maxn];int k[maxn];
int f[maxn];vector<pair<int,int> >max_f[maxn];
int n,d;
void dfs1(int u,int per_u) {for(auto [v,w]:edge[u]) {if(v==per_u) continue;dfs1(v,u);max_f[u].push_back({f[v]+w,v});}sort(max_f[u].begin(),max_f[u].end());reverse(max_f[u].begin(),max_f[u].end());for(int i=0;i<min((int)max_f[u].size(),k[u]-1);i++) {f[u]+=max_f[u][i].first;}	
}
int dp[maxn];int check[maxn];int fa_val[maxn];
void dfs2(int u,int per_u) {int sum1=0,sum2=0,sum3=0;for(int i=0;i<max_f[u].size();i++) {int v=max_f[u][i].second;int w=max_f[u][i].first;if(i<k[u]-1) sum1+=w,check[v]=1;if(i<k[u]) sum2+=w;if(i<k[u]-2) sum3+=w;}dp[u]+=sum3;for(auto [v,w]:edge[u]) {if(v==per_u) continue;if(k[v]==1) continue;if(check[v]) {if(u!=d) fa_val[v]=fa_val[u]+sum1-f[v]-w+w;else fa_val[v]=fa_val[u]+sum2-f[v]-w+w;				dp[v]=fa_val[v];}else {if(u!=d) fa_val[v]=fa_val[u]+sum3+w;else fa_val[v]=fa_val[u]+sum1+w;	dp[v]=fa_val[v];}	dfs2(v,u);}
}
int main() {n=read();d=read();for(int i=1;i<n;i++) {int u=read();int v=read();int w=read();edge[u].push_back({v,w});edge[v].push_back({u,w});}for(int i=1;i<=n;i++) {k[i]=read();}dfs1(d,0);dfs2(d,0);int ans=f[d];for(int i=1;i<=n;i++) {ans=max(ans,dp[i]);}cout<<ans<<endl;return 0;	
}

相关文章:

较难的换根dp:P6213 「SWTR-04」Collecting Coins

传送门 前题提要:感觉这道换根dp可以说是集中了换根dp的所有较高难度的操作和思想,以及较高的一些实现细节,可以说能够完全写出这道题才叫真正理解了换根dp,非常值得一做. 首先读完题意,不难发现这道题有很多限制.点的访问次数限制,必须访问某一个点,想要获得最大的贡献,没有…...

Springboot - 15.二级分布式缓存集成-Caffeine

&#x1f440;中文文档 Caffeine &#x1f440;使用Caffeine &#xff08;本地缓存&#xff09; 当与Spring Boot结合使用时&#xff0c;Caffeine提供了一个直观且功能强大的二级缓存解决方案。Spring Boot的缓存抽象使得整合Caffeine变得相当简单。以下是如何在Spring Boot…...

二叉树的介绍及二叉树的链式结构的实现(C语言版)

前言 二叉树是一种特殊的树&#xff0c;它最大的度为2&#xff0c;每个节点至多只有两个子树。它是一种基础的数据结构&#xff0c;后面很多重要的数据结构都是依靠它来进行实现的。了解并且掌握它是很重要的。 目录 1.二叉树的介绍 1.1概念 1.2现实中的二叉树 1.3特殊的二叉…...

不同写法的性能差异

“ 达到相同目的,可以有多种写法,每种写法有性能、可读性方面的区别,本文旨在探讨不同写法之间的性能差异 len(str) vs str "" 本部分参考自: [问个 Go 问题&#xff0c;字符串 len 0 和 字符串 "" &#xff0c;有啥区别&#xff1f;](https://segmentf…...

Bytebase 2.7.0 - ​新增分支(Branching)功能

&#x1f680; 新功能 新增支持与 Git 类似的分支&#xff08;Branching&#xff09;功能来管理 schema 变更。支持搜索所有历史工单。支持导出审计日志。 &#x1f384; 改进 变更数据库工单详情页面全新改版。优化工单搜索体验。SQL 审核规则支持针对不同数据库进行独立配…...

day55 动规.p15 子序列

- 392.判断子序列 cpp class Solution { public: bool isSubsequence(string s, string t) { vector<vector<int>> dp(s.size() 1, vector<int>(t.size() 1, 0)); for (int i 1; i < s.size(); i) { for (int j 1; …...

TypeScript DOM类型的声明

TS DOM类型的声明 lib.dom.d.ts HTMLInputElement <input type"text" change"handleChange" /> const handleChange (evt: Event) > {console.log((evt.target as HTMLInputElement).value); } HTMLElement const div: HTMLDivElement do…...

springboot找不到注册的bean

1、错误描述 A component required a bean named ‘fixedAssetsShareMapper’ that could not be found.Action:Consider defining a bean named ‘fixedAssetsShareMapper’ in your configuration.2、问题分析 1、该错误提示表明在你的应用程序中有一个组件&#xff08;可能…...

MEMS传感器的原理与构造——单片式硅陀螺仪

一、前言 机械转子式陀螺仪在很长的一段时间内都是唯一的选项&#xff0c;也正是因为它的结构和原理&#xff0c;使其不再适用于现代小型、单体、集成式传感器的设计。常规的机械转子式陀螺仪包括平衡环、支撑轴承、电机和转子等部件&#xff0c;这些部件需要精密加工和…...

Redis集群服务器

集群简介 试想有一家餐厅&#xff0c;如果顾客人数较少&#xff0c;那么餐厅只需要一个服务员即可&#xff0c;如图1。但是&#xff0c;当顾客人数非常多时&#xff0c;一个服务员是绝对不够的&#xff0c;如图2。此时&#xff0c;餐厅需要雇用更多的服务员来解决大量访问&…...

动态维护直径 || 动态维护树上路径 || 涉及LCA点转序列 || 对欧拉环游序用数据结构维护:1192B

https://www.luogu.com.cn/problem/CF1192B 对于直径的求法&#xff0c;常用dp或两次dfs&#xff0c;但如果要动态维护似乎都不太方面&#xff0c;那么可以维护树上路径最大值。 树上路径为&#xff1a; d e p u d e p v − 2 d e p l c a ( u , v ) dep_udep_v-2\times de…...

MySQL 存储引擎,你了解几个?

引言 MySQL是一种流行的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它支持多种不同的数据库引擎。数据库引擎是用于存储、管理和检索数据的核心组件&#xff0c;它们直接影响着数据库的性能、可靠性和功能&#xff0c;接下来本文介绍下一些常见的MySQL数据…...

Java 动态规划 Leetcode 740. 删除并获得点数

题目 对于该题的题目分析&#xff0c;已经代码分析都一并写入到了代码注释中 代码 class Solution {public int deleteAndEarn(int[] nums) {//核心思路&#xff1a;//由于我们获得 nums[i] 的点数之后&#xff0c;就必须删除所有等于 nums[i] - 1 和 nums[i] 1 的元素//假设…...

算法通关村十三关-青铜:数字与数学基础问题

1.数字统计专题 统计特定场景下的符号或数字个数等 1.1符号统计 LeetCode1822 数组元素积的符号 https://leetcode.cn/problems/sign-of-the-product-of-an-array/description/ 思路分析 如果将所有的数都乘起来&#xff0c;再判断正负&#xff0c;工作量大&#xff0c;还…...

猜拳游戏小程序源码 大转盘积分游戏小程序源码 积分游戏小程序源码

简介&#xff1a; 猜拳游戏大转盘积分游戏小程序前端模板源码&#xff0c;一共五个静态页面&#xff0c;首页、任务列表、大转盘和猜拳等五个页面 图片&#xff1a;...

【Python】爬虫练习-爬取豆瓣网电影评论用户的观影习惯数据

目录 前言 一、配置环境 1.1、 安装Python 1.2、 安装Requests库和BeautifulSoup库 1.3.、安装Matplotlib 二、登录豆瓣网&#xff08;重点&#xff09; 2.1、获取代理 2.2、测试代理ip是否可用 2.3、设置大量请求头随机使用 2.4、登录豆瓣网 三、爬取某一部热门电影…...

webpack基础配置【总结】

webpack打包原理&#xff1a; webpack是一个js应用程序的静态模块打包工具&#xff0c;当webpack处理应用程序时&#xff0c;它的内部构建一个依赖图&#xff0c;此时依赖会映射项目中所需的每个模块&#xff0c;并生成一个或多个bundle包。因此我们会安装配置各种打包规则&…...

typescript 支持与本地调试

typescript 支持与本地调试 typescript 支持与本地调试 前言支持 typescript函数的本地调试 启用 node-terminal 调试invoke localserverless-offline Next Chapter完整示例及文章仓库地址 前言 在上一章节&#xff0c;我们创建了一个 hello world 函数&#xff0c;并把它顺…...

后端面试话术集锦第 十八 篇:JVM面试话术

这是后端面试集锦第十八篇博文——JVM面试话术❗❗❗ 1. 介绍下JVM JVM主要包括:类加载器(class loader)、执行引擎(exection engine)、本地接口(native interface)、运行时数据区(Runtimedata area) 类加载器:加载类文件到内存。Class loader只管加载,只要符合文件…...

“历久弥新 | 用AI修复亚运珍贵史料”活动震撼来袭!

时隔近半个世纪&#xff0c;新中国第一次参与亚运会的影像资料将首次对外披露。只是年代久远&#xff0c;老照片老视频都有了岁月痕迹&#xff0c;画面不再清晰&#xff0c;这些珍贵史料急需你的帮助&#xff01; 一、活动介绍 2023年&#xff0c;正值亚运110周年&#xff0c…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

rknn toolkit2搭建和推理

安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...

从零开始了解数据采集(二十八)——制造业数字孪生

近年来&#xff0c;我国的工业领域正经历一场前所未有的数字化变革&#xff0c;从“双碳目标”到工业互联网平台的推广&#xff0c;国家政策和市场需求共同推动了制造业的升级。在这场变革中&#xff0c;数字孪生技术成为备受关注的关键工具&#xff0c;它不仅让企业“看见”设…...

Linux中INADDR_ANY详解

在Linux网络编程中&#xff0c;INADDR_ANY 是一个特殊的IPv4地址常量&#xff08;定义在 <netinet/in.h> 头文件中&#xff09;&#xff0c;用于表示绑定到所有可用网络接口的地址。它是服务器程序中的常见用法&#xff0c;允许套接字监听所有本地IP地址上的连接请求。 关…...