题目描述

https://www.lydsy.com/JudgeOnline/problem.php?id=4585

题解

这题太神了。

我们可以先列出一个dp方程,dp[x][d]表示x节点到所有叶子的距离的d时的代价。

结论1:对于每个点来说,这个dp数组为二维平面上是一个下凸函数。

证明:对于叶子来说一定成立,在w[x]处为0,然后小于w[x]的部分斜率为-1,大与w[x]的部斜率为1。

对于非叶子节点 ,它的函数时有儿子们加起来的,也成立。

然后我们考虑一个点如何向父亲转移。

这是要分四种情况,假设当前节点的斜率为0的部分为L~R。

x<=L f'(x)=f(x)+w[u]

因为边权不能为负,所以我们只能从小的地方往大的地方转移。

比如这个蓝点,它只能从它以及前面的地方转移,但从自己转移时最优的。

x>=L&&x<=L=w[u] f'(x)=f(L)+w-(x-L)

在L处答案为f(L)+w,没往右动一格代价会-1。

x>=L+w[u]&&x<=R+w[u] f'(x)=f(L)

这个相当于直接转移了,没有代价。

x>=R+w[u] f'(x)=f(x)+(x-R)-w

相当于是走过了,会产生代价。

我们发现转移大概长这样(继续盗图)。

我们把左边的点向上动一段后插入斜率为-1的线,再把斜率为0的部分向右平移,最后面是斜率为1的线。

然后这种操作就可以维护了。

思路大概就是只维护拐点,用一个可并堆,每次把右边的部分弹掉。

结论2:每次合并到一个非叶子节点时,斜率为0的线右边有n个点,n为该点的儿子数。

证明:因为最后那一块斜率一定为n(n个斜率为1的直线相加),然后往前经过一个拐点,斜率会-1。

然后把LR取出来做平移,再插回去,最后一直合并到1。

结论3:每经过一个拐点,斜率-1(其实它和上面的结论一个意思)。

然后就可以利用最后的拐点直接算答案了。

怎么算呢?我们把斜率>=0的直线弹掉,把前面的所有直线斜率+1,那么每条直线都会产生deltax的代价,总共有xn的代价,那我们就在答案里+xn,此时最后一条直线斜率为0,把它删掉。

然后一直做,最后得到的是f(0)-f(min),f(0)就是所有边权之和,那么f(min)就可以求出来了。

代码

#include<iostream>
#include<cstdio>
#define N 600002
using namespace std;
typedef long long ll;
int deep[N],d[N],n,m,fa[N],T[N],tot;
ll sum,w[N];
inline ll rd(){
ll x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct tr{
ll v;int l,r;
}tr[N];
int merge(int x,int y){
if(!x||!y)return x^y;
if(tr[x].v<tr[y].v)swap(x,y);
tr[x].r=merge(tr[x].r,y);
if(deep[tr[x].l]<deep[tr[x].r])swap(tr[x].l,tr[x].r);
deep[x]=deep[tr[x].r]+;
return x;
}
inline int pop(int x){return merge(tr[x].l,tr[x].r);}
int main(){
n=rd();m=rd();
for(int i=;i<=n+m;++i){
fa[i]=rd();w[i]=rd();sum+=w[i];d[fa[i]]++;
}
for(int i=n+m;i>=;--i){
ll l=,r=;
if(i<=n){
while(--d[i])T[i]=pop(T[i]);
l=tr[T[i]].v;T[i]=pop(T[i]);
r=tr[T[i]].v;T[i]=pop(T[i]);
}
tr[++tot].v=l+w[i];tr[++tot].v=r+w[i];
T[i]=merge(T[i],merge(tot-,tot));
T[fa[i]]=merge(T[fa[i]],T[i]);
}
while(d[]--)T[]=pop(T[]);
while(T[]){sum-=tr[T[]].v;T[]=pop(T[]);}
cout<<sum;
return ;
}

[APIO2016]烟火表演的更多相关文章

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

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

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

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

  3. 【APIO2016】烟火表演

    题面 题解 神仙题目啊QwQ 设\(f_i(x)\)表示以第\(i\)个点为根的子树需要\(x\)秒引爆的代价. 我们发现,这个函数是一个下凸的一次分段函数. 考虑这个函数合并到父亲节点时会发生怎样的 ...

  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. Eclipse支持文件UTF-8编码

    Eclipse修改编码格式_百度经验https://jingyan.baidu.com/article/2009576193ee38cb0721b416.html 这篇最棒 如何为eclipse中的文 ...

  2. [转帖]windows+xshell+xming访问非桌面版Linux服务器

    windows+xshell+xming访问非桌面版Linux服务器 2016年06月05日 00:09:11 jxxiaohou 阅读数:11996 标签: Linux 更多 个人分类: Linux ...

  3. C# Note10: AutoComplete TextBox in WPF

    参考: 1.https://stackoverflow.com/questions/950770/autocomplete-textbox-in-wpf 2.AutoCompleteBox的使用(实现 ...

  4. CBV源码分析+APIVIew源码分析

    {drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制} 一.CBV源码分析准备工作: 新建一个Django项目 写 ...

  5. java类型的小知识List 等

    List 复制之 浅拷贝与深拷贝 详细连接https://blog.csdn.net/never_tears/article/details/79067245 java中判断字符串是否为数字的方法的几 ...

  6. python数据结构与算法第十天【插入排序】

    1.插入排序的原理 2.代码实现 def insert_sort(alist): # 从第二个位置,即下标为1的元素开始向前插入 for i in range(1, len(alist)): # 从第 ...

  7. Lodop文本项相对于文本框居中 两端对齐

    Lodop中ADD_PRINT_TEXT默认内容是相对于文本框居左的,如果想要设置相对于文本框居中,可用如下语句.还有一种是两端对齐,可以让内容的两端阿和文本框的最左和最右端对齐,文本项内容布满文本框 ...

  8. iOS的非常全的三方库,插件,大牛博客

    转自: http://www.cnblogs.com/zyjzyj/p/6015625.html github排名:https://github.com/trending, github搜索:http ...

  9. ubuntu终端快捷键

    ctrl+alt+t 新终端 ctrl+shift+t打开新的标签页 ctrl+d关闭终端 ctrl+s 暂停屏幕输出 ctrl+q 继续屏幕输出 ctrl+l 清屏 ctrl+alt+f1 切换到第 ...

  10. redis日常使用汇总--持续更新

    redis日常使用汇总--持续更新 工作中有较多用到redis的场景,尤其是触及性能优化的方面,传统的缓存策略在处理持久化和多服务间数据共享的问题总是不尽人意,此时引入redis,但redis是单线程 ...