【APIO2016】烟火表演
题面
题解
神仙题目啊QwQ
设\(f_i(x)\)表示以第\(i\)个点为根的子树需要\(x\)秒引爆的代价。
我们发现,这个函数是一个下凸的一次分段函数。
考虑这个函数合并到父亲节点时会发生怎样的变化。
设\(f_i'(x)\)是原函数,\(f_i(x)\)是新函数,\(i\)和父亲之间的边长度为\(l\),\([L, R]\)是\(f_i'(x)\)斜率为\(0\)的那一段的左右端点的横坐标,那么有:
\begin{cases}
f_i'(x) + l & x \leq L \\
f_i'(L) + (l - (x - L)) & L < x \leq L + l \\
f_i'(L) & L + l < x \leq R + l \\
f_i'(L) + ((x - R) - l) & R + l < x
\end{cases}
\]
我们一个一个来看。
首先第一个,当\(x \leq L\)时,我们肯定要让新的\(l\)越小越好,因为改变\(l\)的代价为\(1\),而这个函数在\(\leq L\)的时候斜率\(\leq -1\),即修改一次\(x\)的代价\(\geq 1\),所以干脆将\(l\)变成\(0\)。
第二个,我们只要保证\(x = L\)就能取到函数的最小值,于是\(l\)的变化量越小越好。
第三个,我们不用改变\(l\)就可以保证能取到最小值,那就不用改变了。
第四个和第一个很像,这里就略去了。
那么这个过程究竟对这个函数做了什么改变呢?
我们将\(\leq L\)部分的函数向上平移了\(l\)单位,将\([L,R]\)部分向右平移\(l\)单位,在\([L,L+l]\)部分插入了一条斜率为\(-1\)的直线,并将\(> R + l\)的部分的斜率改为了\(1\)。
于是大概变成了这个样子(图源网络):
这样,各个拐点之间的直线的斜率是从左到右递增的。
我们不妨假设各个拐点之间的直线斜率的增量为\(1\),如果有一个斜率不存在,那么我们就用两个同一位置的拐点来表示这个不存在的斜率。
然后我们发现我们只可能存下拐点的横坐标,于是怎么求函数值是一个问题。
我们如果能知道\(f(0)\)的值,这个事情就好办了。
\(f(0)\)的值还不好求???就是所有边权之和啊。
于是我们得到了通过拐点横坐标求得\(f(L)\)的方法,皆大欢喜。
那么我们知道每个函数被合并上去之前会变成什么样子了,那么我们也可以非常简单的合并两个函数了,我们只需要将两个函数的拐点列表合并一下就可以了。
我们再看看在合并到父亲节点时要做的操作:
一、将斜率\(> 0\)的那一段的斜率改为\(1\)。
因为我们合并上来的函数的斜率最大值都为\(1\),所以我们只需要删除\(k - 1\)个最大的拐点即可,其中\(k\)是这个点儿子的数量。
二、将斜率\(=0\)的那一段平移\(l\)单位。
首先,我们做完一操作之后,横坐标最大的两个拐点就是斜率为\(0\)的两个端点了,将它们弹出来,加上\(l\)再放进去就没了。
三、加入一段斜率为\(-1\)的直线。
这个其实在做操作二的时候就顺带做完了。
我们维护一个可并堆就可以做上面的所有操作。
最后求答案时,我们保留\(L\)及其左边的拐点,依次减去它们的横坐标就是我们想要的函数值了。
代码
代码倒是比想象中的要短。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
namespace IO
{
const int BUFSIZE = 1 << 20;
char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
inline char getchar() { if (is == it) it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin); return *is++; }
}
inline int read()
{
int data = 0, w = 1;
char ch = IO::getchar();
while(ch != '-' && (ch < '0' || ch > '9')) ch = IO::getchar();
if(ch == '-') w = -1, ch = IO::getchar();
while(ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = IO::getchar();
return data * w;
}
const int maxn(600010);
int n, m, cur, fa[maxn], C[maxn], deg[maxn], rt[maxn];
struct node { int lson, rson, dis; long long x; } t[maxn];
long long ans;
int merge(int x, int y)
{
if(!x || !y) return x | y;
if(t[x].x < t[y].x) std::swap(x, y);
t[x].rson = merge(t[x].rson, y);
if(t[t[x].lson].dis < t[t[x].rson].dis) std::swap(t[x].lson, t[x].rson);
if(!t[x].rson) t[x].dis = 0; else t[x].dis = t[t[x].rson].dis + 1;
return x;
}
inline int pop(int x) { return merge(t[x].lson, t[x].rson); }
int main()
{
#ifndef ONLINE_JUDGE
file(cpp);
#endif
n = read(), m = read();
for(RG int i = 2; i <= n + m; i++)
++deg[fa[i] = read()], ans += (C[i] = read());
for(RG int i = n + m; i > 1; i--)
{
long long l = 0, r = 0;
if(i <= n)
{
while(--deg[i]) rt[i] = pop(rt[i]);
r = t[rt[i]].x, rt[i] = pop(rt[i]);
l = t[rt[i]].x, rt[i] = pop(rt[i]);
}
t[++cur].x = l + C[i], t[++cur].x = r + C[i];
rt[i] = merge(rt[i], merge(cur, cur - 1));
rt[fa[i]] = merge(rt[fa[i]], rt[i]);
}
while(deg[1]--) rt[1] = pop(rt[1]);
while(rt[1]) ans -= t[rt[1]].x, rt[1] = pop(rt[1]);
printf("%lld\n", ans);
return 0;
}
【APIO2016】烟火表演的更多相关文章
- [APIO2016]烟火表演
题目描述 https://www.lydsy.com/JudgeOnline/problem.php?id=4585 题解 这题太神了. 我们可以先列出一个dp方程,dp[x][d]表示x节点到所有叶 ...
- bzoj 4585: [Apio2016]烟火表演【左偏树】
参考:https://blog.csdn.net/wxh010910/article/details/55806735 以下课件,可并堆部分写的左偏树 #include<iostream> ...
- 洛谷P3642 [APIO2016]烟火表演
传送门 题解 fy大佬好强……我根本看不懂…… //minamoto #include<bits/stdc++.h> #define ll long long using namespac ...
- bzoj 4585 烟火表演 - 动态规划 - 可并堆
题目传送门 传送门I 传送门II 题目大意 给定一棵带边权有根树,修改一条边的边权的代价是修改前和修改后的值的绝对值之差.不能将一条边的边权改为负数.问使得根节点到所有叶节点的距离相等的最小代价. 当 ...
- 【APIO2016】Fireworks[DP 可并堆维护凸包优化]
4585: [Apio2016]烟火表演 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 100 Solved: 66[Submit][Status] ...
- UOJ #205/BZOJ 4585 【APIO2016】Fireworks 可并堆+凸包优化Dp
4585: [Apio2016]烟火表演 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 115 Solved: 79[Submit][Status] ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- test20190909 Gluttony
0+0+0+0+0+0=0.毒瘤出题人. BJOI2019 勘破神机 地灾军团的军师黑袍从潜伏在精灵高层的密探手中得知了神杖的情报,他对奥术宝石中蕴含的远古神秘力量十分感兴趣.他设计夺取了数块奥术宝石 ...
- [笔记] Slope Trick:解决一类凸代价函数的DP优化问题
原理 当序列 DP 的转移代价函数满足 连续: 凸函数: 分段线性函数. 时,可以通过记录分段函数的最右一段 \(f_r(x)\) 以及其分段点 \(L\) 实现快速维护代价的效果. 如:$ f(x) ...
随机推荐
- Django ModelForm 小实例1
1.models.py ASSET_STATUS = ( (str(1), u"使用中"), (str(2), u"未使用"), (str(3), u" ...
- [20171130]关于rman备份疑问.txt
[20171130]关于rman备份疑问.txt --//前面测试太乱,重新做一些rman as copy相关测试. 1.环境:SCOTT@book> @ &r/ver1PORT_STR ...
- ws协议的配置
server { listen 80; server_name 域名或IP; rewrite ^(.*)$ https://$host$1 permanent; } server { listen 4 ...
- [SequenceFile_4] SequenceFile 配置压缩
0. 说明 SequenceFile 配置压缩编解码器 && 压缩类型的选型 1. SequenceFile 配置压缩编解码器 package hadoop.compression; ...
- tkinter学习系列(四)之Button 控件
目录 目录 前言 (一)基本用法和可选属性 ==1.基本用法== ==2.可选属性== (二)属性的具体实现和案例 ==1.常用属性== ==案例一== ==2.按钮里的图片== ==案例二== == ...
- JSON语法规则
JSON 语法规则 JSON 语法是 JavaScript 对象表示法语法的子集. 数据在名称/值对中 数据由逗号分隔 花括号保存对象 方括号保存数组 JSON 名称/值对 JSON 数据的书写格式是 ...
- leetcode 395. Longest Substring with At Least K Repeating Characters(高质量题)
只能说还是太菜,抄的网上大神的做法: idea: mask 的每一位代表该位字母够不够k次,够k次为0,不够为1 对于每一位将其视为起点,遍历至末尾,找到其最大满足子串T的下标max_idx,之后从m ...
- java集合类List
1.List Vector:线程安全的. ArrayList:适合查找与顺序添加. LinkedList:适合随机插入与删除. 1.1ArrayList与LinkedList的add添加 1.1.1A ...
- Arcgis10.3在添加XY数据时出现问题
准备通过excel表格(xls格式)中的经纬度生成点数据,但是选择数据的时候报错:连接到数据库失败,常规功能故障,外部表不是预期的格式.如下图所示: 解决方法: 将xls格式的表格另存为csv格式,重 ...
- Hadoop优化 第一篇 : HDFS/MapReduce
比较惭愧,博客很久(半年)没更新了.最近也自己搭了个博客,wordpress玩的还不是很熟,感兴趣的朋友可以多多交流哈!地址是:http://www.leocook.org/ 另外,我建了个QQ群:3 ...