当前位置: 首页 > 编程日记 > 正文

UOJ#7. 【NOI2014】购票 | 线段树 凸包优化DP

题目链接

UOJ #7

题解

首先这一定是DP!可以写出:
\[f[i] = \min_{ancestor\ j} \{f[j] + (d[j] - d[i]) * p[i] + q[i]\}\]
其中\(d[i]\)表示树上\(i\)的深度。
整理一下式子:
\[f[i] = \min_{ancestor\ j} \{f[j] - d[j] * p[i]\} + d[i] * p[i] + q[i]\]
看起来可以斜率优化?
推一下式子:设\(j < k\)\(i\)\(j\)转移优于从\(k\)转移:
\[f[j] - d[j] * p[i] < f[k] - d[k] * p[i]\]
\[\frac{f[k] - f[j]}{d[k] - d[j]} > p[i]\]
好的!
所以应该维护一个下凸壳,在上面二分即可。

可是由于限制条件,每个结点\(i\)对应的下凸壳都是不同的,怎么办呢?

考虑一条链的情况:每个\(f[i]\)都是可以由一个区间内的凸包得到。

可以用线段树维护当前处理完的所有点的凸包,线段树上每个节点上存储着一个凸包,查询的时候相当于在线段树上区间查询——如果当前节点所代表的区间完全包含在查询区间里面,则在这个凸包上二分查询这个区间可以带来的最优解,否则递归,就可以得到答案了。

现在再考虑把一条链上的情况推广到树上。

考虑DFS,栈中的节点组成从根到当前节点的一条链,如果线段树维护了这条链的信息,则可以像正常序列上的情况一样求当前点的\(f\)值。
如果当前点DFS完毕出栈时,可以在线段树上删除它,就可以不影响复杂度地保证时时刻刻线段树维护的都是栈中所有节点的信息,就可以求出答案了。

于是引入【可撤销的凸包】,每次可以【撤销】——回到上一次插入新节点的操作之前。
怎么实现?当插入一个新节点时,二分它要放到哪个位置,并记录当前的top和被新节点取代的节点是谁,当撤销这次插入时,恢复top和被取代的节点即可。这样插入是\(O(\log n)\)的,撤销操作是\(O(1)\)的,非常科学 =v=

代码比想象中好写(雾

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){char c;bool op = 0;while(c = getchar(), c < '0' || c > '9')if(c == '-') op = 1;x = c - '0';while(c = getchar(), c >= '0' && c <= '9')x = x * 10 + c - '0';if(op) x = -x;
}
template <class T>
void write(T x){if(x < 0) putchar('-'), x = -x;if(x >= 10) write(x / 10);putchar('0' + x % 10);
}const int N = 200005;
int n, T, adj[N], nxt[N], fa[N], d_top, id[N];
ll f[N], d[N], p[N], q[N], w[N], lim[N], d_stk[N];
int seg_dep[4*N], pre[N][20], last_top[N][20], top[4*N];
vector <int> stk[4*N];
typedef long double ld;
//pre[i][j]是i号节点在线段树上第j层加入所属的凸包中时, "取代"的点的编号, last[i][j]是加入前该凸包的大小
void build(int k, int l, int r){seg_dep[k] = seg_dep[k >> 1] + 1;stk[k].resize(r - l + 3);if(l == r) return;int mid = (l + r) >> 1;build(k << 1, l, mid);build(k << 1 | 1, mid + 1, r);
}
bool cmp1(int i, int j, int k){ // 判断(i, j, k)是否构成上凸return (ld)(d[j] - d[i]) * (f[k] - f[j]) < (ld)(f[j] - f[i]) * (d[k] - d[j]);
}
int find_pos(int k, int i){ // 在k号单调栈中插入i号点, 应该放在哪个位置if(!top[k]) return 1;int l = 2, r = top[k] + 1, mid; // 找到第一个和i上凸的位置(新来的i应该取代这个位置)while(l < r){mid = (l + r) >> 1;if(cmp1(stk[k][mid - 1], stk[k][mid], i)) r = mid;else l = mid + 1;}return l;
}
void push(int k, int i){int p = find_pos(k, i);last_top[i][seg_dep[k]] = top[k];pre[i][seg_dep[k]] = stk[k][p];top[k] = p;stk[k][p] = i;
}
void rollback(int k){int i = stk[k][top[k]];stk[k][top[k]] = pre[i][seg_dep[k]];top[k] = last_top[i][seg_dep[k]];
}
ll calc(int u, int v){return f[u] + (d[v] - d[u]) * p[v] + q[v];
}
bool cmp2(int i, int j, ll x){ // 判断i和j构成的斜率是否小于等于xreturn f[j] - f[i] <= (ld) x * (d[j] - d[i]);
}
ll ask(int k, int x){int l = 1, r = top[k];while(l < r){int mid = (l + r) >> 1;if(cmp2(stk[k][mid], stk[k][mid + 1], p[x])) l = mid + 1;else r = mid;}return calc(stk[k][l], x);
}
void insert(int k, int l, int r, int p, bool flag){flag ? push(k, id[p]) : rollback(k);if(l == r) return;int mid = (l + r) >> 1;if(p <= mid) insert(k << 1, l, mid, p, flag);else insert(k << 1 | 1, mid + 1, r, p, flag);
}
ll query(int k, int l, int r, int ql, int qr){if(ql <= l && qr >= r) return ask(k, id[qr + 1]);int mid = (l + r) >> 1;ll ret = 9e18;if(ql <= mid) ret = query(k << 1, l, mid, ql, qr);if(qr > mid) ret = min(ret, query(k << 1 | 1, mid + 1, r, ql, qr));return ret;
}
void dfs(int u){d_stk[++d_top] = d[u] = d[fa[u]] + w[u], id[d_top] = u;if(u != 1){int st = lower_bound(d_stk + 1, d_stk + d_top + 1, d[u] - lim[u]) - d_stk;f[u] = query(1, 1, n, st, d_top - 1);}insert(1, 1, n, d_top, 1);for(int v = adj[u]; v; v = nxt[v]) dfs(v);insert(1, 1, n, d_top, 0);d_top--;
}int main(){read(n), read(T);build(1, 1, n);for(int i = 2; i <= n; i++){read(fa[i]), read(w[i]), read(p[i]), read(q[i]), read(lim[i]);nxt[i] = adj[fa[i]], adj[fa[i]] = i;}dfs(1);for(int i = 2; i <= n; i++)write(f[i]), enter;return 0;
}

转载于:https://www.cnblogs.com/RabbitHu/p/UOJ7.html

相关文章:

python中集合的元素可以是任意数据类型_Python之基本数据类型——集合数据类型...

集合set(可变的数据类型)&#xff1a; 数据结构以大括号{}表示&#xff0c;各元素逗号隔开&#xff0c;例&#xff1a;{1,2,3,4}。 集合特征&#xff1a;无序&#xff0c;元素不重复 创建集合&#xff1a; s{1,2,3} pirnt(s) #---------------{1,2,3} sset(hello) print(s) #--…

uv_timer_t的释放问题

项目中的计时器模块是用libuv做的&#xff0c;今天发现了点问题&#xff0c;是释放uv_timer_t引起了&#xff0c;我是在uv_timer_start的回调里释放该结构的&#xff0c;这里是不能释放了&#xff0c;因为回调完后&#xff0c;库还会使用uv_timer_t里的数据&#xff0c;之前没出…

区块链分支循环

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 分支循环 程序的流程控制结构一共有三种&#xff1a;顺序结构&#xff0c;选择结构&#xff0c;循环结构。 一、条件语句 1.1 If语句 语法格式…

c和python区别_C语言和python的区别

Python可以说是目前最火的语言之一了&#xff0c;人工智能的兴起让Python一夜之间变得家喻户晓&#xff0c;Python号称目前最最简单易学的语言&#xff0c;现在有不少高校开始将Python作为大一新生的入门语言。本萌新也刚开始接触Python&#xff0c;发现Python与其他语言确实有…

(1)访问控制 (2)final关键字 (3)对象创建的过程 (4)多态

1.访问控制(笔试题)1.1 常用的访问控制符 public - 公有的 protected - 保护的 啥也不写 - 默认的 private - 私有的 1.2 访问控制符的比较 访问控制符 访问权限 本类 本包中的类 子类 其他包的类---------------------------------------------------------------------------…

MySQL安装ODBC驱动出现126错误

需求&#xff1a;MySQL导入ODBC文件&#xff0c;需要安装ODBC驱动。 问题&#xff1a;本机的MySQL是5.0版本&#xff0c;刚开始下载的是5.3ODBC&#xff0c;然后出现以下错误&#xff1a; 解决方法&#xff1a;ODBC版本应该与MySQL版本一致&#xff0c;重新安装5.0版本的ODBC即…

超级账本的由来

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 1.1.1 超级账本的由来 当你拿起这本书开始阅读的时候&#xff0c;说明你对区块链技术已经有了相关的了解&#xff0c;而且想通过自己的努力或团队合…

你为什么应该经常访问招聘网站?招聘网站至少有4个方面的价值!

一、缘起读大学的时候&#xff0c;有时候会感到很迷茫&#xff0c;不知道毕业之后可以做什么&#xff0c;自己能拿到多少的月薪。于是&#xff0c;就想到去参加一些公司的招聘。大二大三的时候&#xff0c;就去武大参加了武汉中地数码等3个公司的笔试。但是&#xff0c;没有交答…

python去重复行_python去除文件中重复的行实例

python去除文件中重复的行&#xff0c;我们可以设置一个一个空list&#xff0c;res_list&#xff0c;用来加入没有出现过的字符行&#xff01; 如果出现在res_list&#xff0c;我们就认为该行句子已经重复了&#xff0c;可以再加入到记录重复句子的list中。 如下代码&#xff1…

限制TensorFlow只在CPU上运行的方法

笔记本是NVIDIA GeForce 940M的显卡&#xff0c;只有2G的显存&#xff0c;运行TensorFlow代码时候常出现OOM(Out of Memory)的错误&#xff0c;原因是batch_size设置得太大导致显存不足。如果想让代码仅仅运行在CPU下&#xff0c;可在原代码中加入如下代码&#xff1a; import …

比特币挖矿——区块链技术

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 说明 区块链具有数据运行公开、不可篡改、可溯源、跨国际、去中心化的特点。因此越来越多地被应用在各个领域。区块链主要技术包括&#xff1a;分布…

Python黑帽编程2.4 流程控制

Python黑帽编程2.4 流程控制 本节要介绍的是Python编程中和流程控制有关的关键字和相关内容。 2.4.1 if …..else 先上一段代码&#xff1a; #!/usr/bin/python # -*- coding: UTF-8 -*- xint(input(请输入一个整数:)) if x0: print %d 0 % x elif x<0: print %d <0 % x…

【Flask】视图高级

# 视图高级笔记&#xff1a;### add_url_rule(rule,endpointNone,view_funcNone)这个方法用来添加url与视图函数的映射。如果没有填写endpoint&#xff0c;那么默认会使用view_func的名字作为endpoint。以后在使用url_for的时候&#xff0c;就要看在映射的时候有没有传递endpoi…

振动力学基础与matlab应用_【日文好书推荐】振动与噪声控制技术for机械设计者...

声海译读活动日文小组为大家推荐好书&#xff0c;《振动与噪声控制技术for机械设计者》作者&#xff1a;小林英男&#xff0c;欢迎大家围观讨论提出宝贵意见&#xff01;目录译文(一)译者&#xff1a;穆瑞林-天津科技大学前言第一章 机械设计开发•设计者对振动•噪声技术入门所…

区块链是互联网未来十年中举足轻重的技术

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 区块链是互联网未来十年中举足轻重的技术 区块链&#xff08;Blockchain&#xff09;&#xff0c;或者说分布式账本&#xff08;DLT, Distributed …

利用jdt快速实现pmd的功能

jdt可以做语法树分析&#xff0c;并且支持visitor模式对代码进行分析。跟pmd的分析方式一样&#xff0c;我们只要实现 visitor接口即可实现一个插件。 Service("requestMappingInfoService")public class RequestMappingInfoServiceImpl implements RequestMappingIn…

用R语言做词频统计_R语言 | 词频统计

Python网络爬虫与文本数据分析本章内容导入停用词读数据&#xff0c;分词剔除停用词导入停用词表library(dplyr)## [1] "?" "、" "。" "“" "”" "《" "》" "!" "…

PHP拿到别人项目如何修改为自己

以下为借助google翻译的&#xff0c;个人润色了一下&#xff0c;官方版里面感觉有很多问题&#xff0c;我这里有我个人修改大部分问题的版本&#xff0c;包括翻译完善&#xff0c;有需要的可以联系我&#xff1a;qyj8411163.com 1. 在您网站的根目录创建名为“webim”的文件夹。…

浅析Hyperledger Fabric共识算法

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 区块链系统是一个分布式架构&#xff0c;交易账本信息由各个节点管理&#xff0c;组成一个庞大的分布式账本。在分布式系统中&#xff0c;各个节点收…

python 获取用户ip_Python爬虫教程:你还在苦苦拉票吗?刷票小程序案例原理剖析!...

你还在苦苦拉票吗&#xff1f;前言剖析投票原理处理思路具体实战主要流程具体细节python代码实例python具体细节java代码实现java总结点击此处&#xff0c;获取海量Python学习资料&#xff01;前言现在生活中离不开各类的比赛&#xff0c;然而&#xff0c;各个比赛离不开投票&a…

下拉菜单被挡住了,DIV置于最底层的方法

网站常会用到一些 下拉菜单&#xff0c;&#xff0c;幻灯片&#xff0c;&#xff0c;&#xff0c;飘浮广告等。但经常会发现。幻灯片会挡住下拉菜单或者飘浮广告等。解决办法有下第一&#xff0c;可将幻灯片所在DIV 置于最底层。添加CSS如下style"z-index:-100;position: …

github的删除

github项目删除 首先找到你要删除的项目&#xff0c;点击开 复制项目名称然后找到Settings 将滚动条滑至底部&#xff0c;找到 Danger Zone 下的 Delete this repository 这里会弹出一个警告对话框 将该项目名称重新输一遍即可 这里会弹出账号重新确认&#xff0c;将密码在输入…

区块链的去中心化VS传统互联网的去中心化:技术与治理的双重困境

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 区块链的去中心化VS传统互联网的去中心化&#xff1a;技术与治理的双重困境11 主要观点&#xff1a; 1、传统互联网经典的去中心化项目BitTorrent…

第三章| 3.1文件处理

1、三元运算 简单条件语句&#xff1a; if 条件成立:val 1 else: val 2 改成三元运算&#xff1a; val 1 if 条件成立 else 2 2、文件处理 &#xff08;用python对硬盘上的文件操作&#xff09; 读 读取文件的三个方法&#xff1a;read()、readline()、readlines() 三个方法…

LR常见的报错处理方法

1.LR录制时不弹出IE浏览器 当一台主机上装有多个浏览器&#xff0c;LR录制脚本时&#xff0c;经常遇到打不开浏览器的情况&#xff0c;可以用下面的方法来解决 启动浏览器&#xff0c;打开Internet 选项对话框&#xff0c;切换到高级标签&#xff0c;去掉“启用第三方浏览器扩展…

均匀分布取某一点概率_概率和概率分布

概率与概率分布是统计学中的基础概念&#xff0c;在我们的高中的课本中就接触过了&#xff0c;如果有遗忘&#xff0c;一起来回顾一下吧&#xff01;知识点&#xff1a;概率概率分布一、概率说到概率&#xff0c;需要先了解一个概念&#xff0c;叫做随机试验。随机试验是指在相…

EOS共识机制——DPoS代理权益证明

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 区块链共识机制与它的演进&#xff0c;是由于区块链式去中心化而且分布式的系统&#xff0c;必须要有一套放诸四海皆准类似宪法的规则&#xff0c;来…

active mq topic消费后删除_Spring cloud stream 整合mq

说明&#xff1a;本案例win10环境测试scs(spring cloud stream)整合kfk(kafka)/rbt(rabbitmq)消息生产消费场景流程一、准备中间件环境(kfk/rbt)后续内容提供kfk与rbt的环境准备流程二、导入scs的依赖项目spring boot 版本&#xff1a;2.1.3.RELEASE引入spring cloud 版本&…

翻译的艺术 —— 无能为力的翻译,搞笑的音译

0. 无能为力的翻译 至尊宝&#xff1a;best sonny&#xff0c;乌龙&#xff1a;own goal&#xff0c;的粤语发音&#xff1b;1. 取其发音 word ⇒ 我的&#xff0c;word 妈呀&#xff0c; Need just word,word has word&#xff0c;你的就是我的&#xff0c;我的还是我的&#…

经常可能会用到的【函数节流和函数防抖】记录下,做下区分

今天突然被人问到&#xff0c;函数节流和函数防抖的区别是什么&#xff0c;结果我脑子一热直接举了个滚动条的粟子说是优化高频率执行的手段&#xff0c;就记得自己是用setTimeout来实现的。完了区别是什么&#xff1f;&#xff1f;哪个是哪个都蒙B了回家想想&#xff0c;有些东…