题面

题解

神仙题目啊QwQ

设\(f_i(x)\)表示以第\(i\)个点为根的子树需要\(x\)秒引爆的代价。

我们发现,这个函数是一个下凸的一次分段函数。

考虑这个函数合并到父亲节点时会发生怎样的变化。

设\(f_i'(x)\)是原函数,\(f_i(x)\)是新函数,\(i\)和父亲之间的边长度为\(l\),\([L, R]\)是\(f_i'(x)\)斜率为\(0\)的那一段的左右端点的横坐标,那么有:

\[f_i(x) =
\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】烟火表演的更多相关文章

  1. [APIO2016]烟火表演

    题目描述 https://www.lydsy.com/JudgeOnline/problem.php?id=4585 题解 这题太神了. 我们可以先列出一个dp方程,dp[x][d]表示x节点到所有叶 ...

  2. bzoj 4585: [Apio2016]烟火表演【左偏树】

    参考:https://blog.csdn.net/wxh010910/article/details/55806735 以下课件,可并堆部分写的左偏树 #include<iostream> ...

  3. 洛谷P3642 [APIO2016]烟火表演

    传送门 题解 fy大佬好强……我根本看不懂…… //minamoto #include<bits/stdc++.h> #define ll long long using namespac ...

  4. bzoj 4585 烟火表演 - 动态规划 - 可并堆

    题目传送门 传送门I 传送门II 题目大意 给定一棵带边权有根树,修改一条边的边权的代价是修改前和修改后的值的绝对值之差.不能将一条边的边权改为负数.问使得根节点到所有叶节点的距离相等的最小代价. 当 ...

  5. 【APIO2016】Fireworks[DP 可并堆维护凸包优化]

    4585: [Apio2016]烟火表演 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 100  Solved: 66[Submit][Status] ...

  6. UOJ #205/BZOJ 4585 【APIO2016】Fireworks 可并堆+凸包优化Dp

    4585: [Apio2016]烟火表演 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 115  Solved: 79[Submit][Status] ...

  7. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  8. test20190909 Gluttony

    0+0+0+0+0+0=0.毒瘤出题人. BJOI2019 勘破神机 地灾军团的军师黑袍从潜伏在精灵高层的密探手中得知了神杖的情报,他对奥术宝石中蕴含的远古神秘力量十分感兴趣.他设计夺取了数块奥术宝石 ...

  9. [笔记] Slope Trick:解决一类凸代价函数的DP优化问题

    原理 当序列 DP 的转移代价函数满足 连续: 凸函数: 分段线性函数. 时,可以通过记录分段函数的最右一段 \(f_r(x)\) 以及其分段点 \(L\) 实现快速维护代价的效果. 如:$ f(x) ...

随机推荐

  1. GridSearchCV交叉验证

    代码实现(基于逻辑回归算法): # -*- coding: utf-8 -*- """ Created on Sat Sep 1 11:54:48 2018 @autho ...

  2. [MapReduce_3] MapReduce 程序运行流程解析

    0. 说明 Word Count 程序运行流程解析 &&  MapReduce 程序运行流程解析 1. Word Count 程序运行流程解析 2. MapReduce 程序运行流程图

  3. Mysqli面向对象操作数据库

    Mysqli面向对象操作数据库 首先配置一下数据库: 接着用PHP中的Mysqli扩展库面向对象查询这个数据表. 操作分为以下几个步骤: 连接数据库 操作数据库 处理结果 关闭资源 <?php ...

  4. spark-Scala

    一.spark的特点 1.快速的 2.容易使用的 3.通用的 4.开放的 二.spark组件 sparkCore sparkSQL sparkStreaming MLibmachielearning ...

  5. January 29th, 2018 Week 05th Monday

    Losing all hope was freedom. 彻底绝望就是真正的自由. Losing all the hopes, and we are free to challenge everyth ...

  6. January 26th, 2018 Week 04th Friday

    A great forest is set on fire by a small spark. 最小的火能点着最大的树林. It is just a spark, but it is enough t ...

  7. git命令行解决冲突文件步骤

    原文https://blog.csdn.net/zwl18210851801/article/details/79106448 亲测有用,解决git冲突的好办法 方法一(推荐使用): git pull ...

  8. Django之ORM查询

    ORM 映射关系: 表名 <-------> 类名 字段 <-------> 属性 表记录 <------->类实例对象图书管理系统的增删改查:代码如下:views ...

  9. [Jsoi2013]快乐的jyy

    题目 这个需要我们瞎\(yy\)一下就能做了 我们先对于第一个串建立\(PAM\) 我们把第二个串丢上去匹配,这里匹配出来的是以每一个位置为结尾且在另一个串里存在的最长回文后缀的长度 对于每一个位置开 ...

  10. the current differences between MyISAM and InnoDB storage engines

    原文地址:https://stackoverflow.com/questions/47680213/what-are-the-current-differences-between-myisam-an ...