【做题笔记】虚树 (LuoguP2495 - [SDOI2011] 消耗战)

虚树

在给定的树上问题中,树的大小过大,导致总时间复杂度 O ( n q ) O(nq) O(nq)不能被接受,但是有很多点在查询的过程中根本用不到,所以对于每一个查询,建立一个小的查询树,在这个小树上求解问题。这个小树就是虚树。

LuoguP2495 - [SDOI2011] 消耗战

题目链接

如果只要一组数据,那么可以通过 d p dp dp进行求解。

从树的叶子结点向根节点 d p dp dp

如果当前这个节点是关键点,那么这个点必然要被删掉,那么一定是将这个点连带着以这个节点为根节点的子树一并删除,即一定是一刀切。但是不一定只删除这一个子树,也可以连带着上面的点一起删。为了让代价最小,就选择从整棵树的根节点到这个点的路径上代价最小的边删即可。

如果这个点不是关键点,那么可以选择删除这个点,此时代价和上面的情况一样;也可以选择不删除这个点,而是把其子节点删除。子节点是已经 d p dp dp算好的最优结果,所以遍历每一个子节点,计算删除代价即可。(如果这个子节点的子树下没有关键点,那么 d p dp dp后的结果自然是 0 0 0)。

这样从下往上跑一次 d p dp dp,根节点的答案就是这道题目的答案。

但是这道题目 O ( n q ) O(nq) O(nq)不能接受,我们要考虑优化。优化的思路是,我们发现在有些查询当中,有些点是根本不必要的,比如说有几个连续的非关键点,那么这个点的贡献就是传递答案,把这些点删除显然不影响答案。那么我们可以在每次查询中,将关键点单独拎出来做一棵子树,然后跑上述 d p dp dp即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 500005;
const LL INF = 1e18;LL en1, en, n, k, m, si, tot;
//si:栈的当前元素个数
LL front[N], front1[N], a[N], dep[N], fa[N][22], lg[N];
LL stk[N], id[N], mp[N], dp[N], vis[N];
//dep[N]:树上节点的深度(求LCA用)
//fa[N][22]:距离为2的幂数的祖先(求LCA用)
//lg[N]:以2为底的对数
//stk[N]:构建虚树时要用的栈
//id[N]:所有节点的dfs序
//mp[N]:从根节点到这个节点的路径中最小的边权
//dp[N]:存储从下至上删除当前节点的子树内所有关键点的最小代价
//vis[N]:用来存储这个点是不是关键点struct Edge {LL v, w, next;
}e1[N * 2], e[N * 2];//对每一次查询建的虚树
void addEdge(int u, int v) {e[++en] = {v, 0, front[u]};front[u] = en;
}//总体的树
void addEdge1(int u, int v, int w) {e1[++en1] = {v, w, front1[u]};front1[u] = en1;
}void dfs(int u, int f) {//获取所有节点的dfs序id[u] = ++tot;//获取深度dep[u] = dep[f] + 1;fa[u][0] = f;//倍增获取祖先信息for (int i = 1; (1 << i) <= dep[u]; ++i) {fa[u][i] = fa[fa[u][i - 1]][i - 1];}for (int i = front1[u]; i; i = e1[i].next) {LL v = e1[i].v, w = e1[i].w;if (v != f) {//更新根节点到u路径上的最小边权mp[v] = min(mp[u], w);dfs(v, u);}}
}//构建虚树时使用,求最近公共祖先(LCA)
int lca(int x, int y) {if (dep[x] < dep[y]) swap(x, y);while (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]]];if (x == y) return x;for (int k = lg[dep[x]]; k >= 0; --k) {if (fa[x][k] != fa[y][k]) {x = fa[x][k]; y = fa[y][k];}	}return fa[x][0];
}//建立虚树
void build_virtual_tree() {//将关键点按照原题中的树的dfs序进行排列sort(a + 1, a + m + 1, [](const int &A, const int &B) {return id[A] < id[B];});//将根节点推入栈中stk[++si] = 1;front[1] = 0;for (int i = 1; i <= m; ++i) {if (a[i] == 1) continue;//g为a[i]和当前栈顶节点的LCAint g = lca(a[i], stk[si]);//如果LCA不是栈顶节点,说明当前节点进入了其中一个祖先的新的子树//需要将当前栈中原来子树的节点处理掉if (g != stk[si]) {while (id[g] < id[stk[si - 1]]) {//依次建边,并弹出栈addEdge(stk[si - 1], stk[si]);--si;}//如果LCA的dfs序大于栈顶第二个节点的dfs序//说明LCA点不是当前栈顶节点的祖先//不是的需要先推入栈,然后再连边if (id[g] > id[stk[si - 1]]) {front[g] = 0;addEdge(g, stk[si]);stk[si] = g;}else {addEdge(g, stk[si--]);}}front[a[i]] = 0;stk[++si] = a[i];}//栈中剩余的节点都是一条链上的for (int i = 1; i < si; ++i) {addEdge(stk[i], stk[i + 1]);}
}void dfs1(int u) {//如果当前节点是叶子节点if (front[u] == 0) {if (vis[u]) dp[u] = mp[u];else dp[u] = 0;front[u] = vis[u] = 0;return;} //tmp:如果u不是关键点,则统计删除自己所有子树的最小贡献,记为tmpLL tmp = 0;for (int i = front[u]; i; i = e[i].next) {LL v = e[i].v;dfs1(v);tmp += dp[v];}//如果u是关键节点,则只能一刀切,将以u为根节点的子树一并摘除//删除从根节点到u的路径上最短的一条//如果u不是关键点,则可以选择一刀切,也可以选择用tmp,取其中的最小if (vis[u]) dp[u] = mp[u];else dp[u] = min(mp[u], tmp);//求dp对每个节点只访问一次,所以可以直接把状态清空//现在清空可以避免对于每组数据统一清空,时间复杂度变回O(nq)front[u] = 0;vis[u] = 0;
}void main2() {cin >> n;lg[1] = 0; lg[2] = 1;for (int i = 3; i <= n; ++i) {lg[i] = lg[i / 2] + 1;}en1 = 0;for (int i = 1; i <= n; ++i) {front1[i] = id[i] = 0;mp[i] = INF;}for (int i = 1; i < n; ++i) {LL u, v, w;cin >> u >> v >> w;addEdge1(u, v, w);addEdge1(v, u, w); }tot = 0;mp[1] = INF; dfs(1, 0);cin >> k;for (int i = 1; i <= k; ++i) {cin >> m;for (int j = 1; j <= m; ++j) {cin >> a[j];vis[a[j]] = 1;}si = en = 0;build_virtual_tree();dfs1(1);cout << dp[1] << '\n';}
}int main() {ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);LL _ = 1;
//	cin >> _while (_--) main2();return 0;
}

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

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

相关文章

leetcode 941. 有效的山脉数组

2023.9.2 可以用双指针法来做&#xff0c;left指向数组起点&#xff0c;right指向数组终点&#xff0c;left满足条件则左移&#xff0c;right满足条件则右移&#xff0c;最终两指针重合则返回true。 期间任一条件不满足则返回false。 代码如下&#xff1a; class Solution { p…

大数据时代下的数据安全防护

随着大数据时代的来临&#xff0c;数据安全防护成为了一个重要的问题。在大数据时代&#xff0c;数据的规模和价值都得到了极大的提升&#xff0c;因此数据安全的重要性也变得越来越突出。本文将从数据加密、访问控制、网络安全和人员管理四个方面来介绍大数据时代下的数据安全…

点云数据做简单的平面的分割 三维场景中有平面,杯子,和其他物体 实现欧式聚类提取 对三维点云组成的场景进行分割

点云分割是根据空间,几何和纹理等特征对点云进行划分,使得同一划分内的点云拥有相似的特征,点云的有效分割往往是许多应用的前提,例如逆向工作,CAD领域对零件的不同扫描表面进行分割,然后才能更好的进行空洞修复曲面重建,特征描述和提取,进而进行基于3D内容的检索,组合…

什么是盒子模型

什么是盒子模型 盒子模型&#xff0c;也可以称为框模型。 所有 HTML 元素可以看作盒子。在 CSS 中&#xff0c;“box model” 这一术语是用来设计和布局时使用。 CSS 盒模型本质上是一个盒子&#xff0c;封装周围的 HTML 元素&#xff0c;它包括&#xff1a;边距&#xff0c…

8. 损失函数与反向传播

8.1 损失函数 ① Loss损失函数一方面计算实际输出和目标之间的差距。 ② Loss损失函数另一方面为我们更新输出提供一定的依据。 8.2 L1loss损失函数 ① L1loss数学公式如下图所示&#xff0c;例子如下下图所示。 import torch from torch.nn import L1Loss inputs torch.tens…

Django静态文件媒体文件文件上传

文章目录 一、静态文件和媒体文件1.在django中使用静态文件实践2.在django中使用媒体文件 二、文件上传单文件上传实践多文件上传 一、静态文件和媒体文件 媒体文件: 用户上传的文件&#xff0c;叫做media 静态文件:存放在服务器的css,js,image,font等 叫做static1.在django中…

【Locomotor运动模块】瞬移

文章目录 一、原理二、两种类型1、Instant(立刻)2、Dash&#xff08;猛冲&#xff09; 三、瞬移区域、瞬移点1、瞬移区域2、瞬移点 一、原理 抛物线指针选择好目标位置&#xff0c;然后告诉瞬移预设体&#xff1a;你想法把游戏区域弄到目标位置来 解释&#xff1a;抛物线指针选…

JS中的new操作符

文章目录 JS中的new操作符一、什么是new&#xff1f;二、new经历了什么过程&#xff1f;三、new的过程分析四、总结 JS中的new操作符 参考&#xff1a;https://www.cnblogs.com/buildnewhomeland/p/12797537.html 一、什么是new&#xff1f; 在JS中&#xff0c;new的作用是通过…

pytest parametrize多参数接口请求及展示中文响应数据

编写登陆接口 app.py from flask import Flask, request, jsonify, Responseapp Flask(__name__)app.route(/login, methods[POST]) def login():username request.form.get(username)password request.form.get(password)# 在这里编写你的登录验证逻辑if username admin…

量化策略分类:中低频超高频

指数增强策略&#xff08;AlphaBeta&#xff09; 在跟踪标的指数的基础上&#xff0c;用量化投资的方法&#xff0c;适当调整持仓范围&#xff0c;追求获得超越标的指数的收益。 量化选股策略&#xff08;AlphaBeta&#xff09; 通过量化因子&#xff0c;选择股票多头持仓&a…

React笔记(八)Redux

一、安装和配置 React 官方并没有提供对应的状态机插件&#xff0c;因此&#xff0c;我们需要下载第三方的状态机插件 —— Redux。 1、下载Redux 在终端中定位到项目根目录&#xff0c;然后执行以下命令下载 Redux npm i redux 2、创建配置文件 在 React 中&#xff0c;…

FreeRTOS的信号量和互斥量之间的区别和联系

文章目录 信号量信号量简介信号量特征 互斥量互斥量的上锁机制互斥量的优先级继承机制 二值信号量和互斥量的作用二值信号量的作用互斥量的作用 二值信号量和互斥锁关系相同点不同点 如何根据场景选择回答信号量和互斥锁之间的区别&#xff1a; 信号量 信号量简介 队列(queue)…

LeetCode 面试题 02.06. 回文链表

文章目录 一、题目二、C# 题解 一、题目 编写一个函数&#xff0c;检查输入的链表是否是回文的。 点击此处跳转题目。 示例 1&#xff1a; 输入&#xff1a; 1->2 输出&#xff1a; false 示例 2&#xff1a; 输入&#xff1a; 1->2->2->1 输出&#xff1a; true …

大集合按照指定长度进行分割成多个小集合,用于批量多次处理数据

&#x1f4da;目录 拆分案例拆分的核心代码 通常我们对集合的更新或者保存都需要用集合来承载通过插入的效率&#xff0c;但是这个会遇到一个问题就是你不知道那天那个集合的数量可能就超了&#xff0c;虽然我们连接数据库进行批量提交会在配置上配置allowMultiQueriestrue,但是…

(一)连续随机量的生成-接受拒绝重采样

连续随机量的生成-接受拒绝重采样 1. 接受-拒绝重采样方法2. Python编程实现 1. 接受-拒绝重采样方法 重采样方法由两个步骤组成&#xff0c;第一个步骤提供近似分布的随机量&#xff0c;第二个步骤是校正机制。 在下文中&#xff0c;我们用 f f f 表示目标分布&#xff0c;用…

【每日一题】823. 带因子的二叉树

【每日一题】823. 带因子的二叉树 823. 带因子的二叉树题目描述解题思路 823. 带因子的二叉树 题目描述 给出一个含有不重复整数元素的数组 arr &#xff0c;每个整数 arr[i] 均大于 1。 用这些整数来构建二叉树&#xff0c;每个整数可以使用任意次数。其中&#xff1a;每个…

类和对象(下)

&#x1f493;博主个人主页:不是笨小孩&#x1f440; ⏩专栏分类:数据结构与算法&#x1f440; C&#x1f440; 刷题专栏&#x1f440; C语言&#x1f440; &#x1f69a;代码仓库:笨小孩的代码库&#x1f440; ⏩社区&#xff1a;不是笨小孩&#x1f440; &#x1f339;欢迎大…

大模型综述论文笔记1-5

目录 KeywordsIntroductionSLMNLMPLMLLM Backgroud for LLMsScaling Laws for LLMsKM scaling lawChinchilla scaling law Emergent Abilities of LLMsIn-context learningInstruction followingStep-by-step reasoning Key Techniques for LLMsScalingTrainingAbility eliciti…

【uniapp】 实现公共弹窗的封装以及调用

图例&#xff1a;红框区域为 “ 内容区域 ” 一、组件 <!-- 弹窗组件 --> <template> <view class"add_popup" v-if"person.isShowPopup"><view class"popup_cont" :style"{width:props.width&&props.width&…

mybatis源码学习-1-调试环境

写在前面,这里会有很多借鉴的内容,有以下三个原因 本博客只是作为本人学习记录并用以分享,并不是专业的技术型博客笔者是位刚刚开始尝试阅读源码的人,对源码的阅读流程乃至整体架构并不熟悉,观看他人博客可以帮助我快速入门如果只是笔者自己观看,难免会有很多弄不懂乃至理解错误…