[APIO2016]烟火表演
题目描述
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]烟火表演的更多相关文章
- 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 ...
- 【APIO2016】烟火表演
题面 题解 神仙题目啊QwQ 设\(f_i(x)\)表示以第\(i\)个点为根的子树需要\(x\)秒引爆的代价. 我们发现,这个函数是一个下凸的一次分段函数. 考虑这个函数合并到父亲节点时会发生怎样的 ...
- 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) ...
随机推荐
- Centos6.x升级内核方法支持Docker
Centos6升级内核方法_百度经验https://jingyan.baidu.com/article/7e4409531bda252fc1e2ef4c.html
- Java.lang.OutOfMemoryError:Metaspace
Understand the OutOfMemoryError Exceptionhttps://docs.oracle.com/javase/8/docs/technotes/guides/trou ...
- window端编码到Linux允许脚本 笔记
昨天升级一个服务,发现没有现成的启动脚本.就随手写了一个,一运行发现不行.竟然报错说找不到文件,No such file or directory [nohup: cannot run command ...
- java依赖的斗争:依赖倒置、控制反转和依赖注入
控制反转(Inversion Of Controller)的一个著名的同义原则是由Robert C.Martin提出的依赖倒置原则(Dependency Inversion Principle),它的 ...
- 【学亮IT手记】Ajax跨域问题精讲--jQuery解决跨域操作
什么是跨域 跨域,它是不同的域名(服务器)之间的相互的资源之间的访问. 当协议,域名,端口号任意一个不同,它们就是不同的域. 正常情况下,因为浏览器安全的问题,不同域之间的资源是不可以访问的. 跨域的 ...
- Vue 鼠标移入移出事件
Vue 中鼠标移入移出事件 @mouseover和@mouseleave 然后绑定style 现在开始代码示例 <template> <div class="pc&qu ...
- DNS 到底怎么工作的? (How does dns work?)
其实这个问题每次看的时候都觉得很明白,但是很久之后就忘记了,所以这次准备记录下来.深入到这个过程的各个细节之中,以后多看看. Step 1 请求缓存信息: 当你在开始访问一个 www.baidu.co ...
- react用构造函数创建组件
有两种方法,一种是通过构造函数创建,一种是通过class创建 1.构造函数创建组件 用function+组件名的方式创建,创建好了,在render里面以标签的形式一丢就可以啦!但是这种方式必须要ret ...
- 学习 Spring (十六) AOP API
Spring入门篇 学习笔记 Spring AOP API 是 Spring 1.2 历史用法,现在仍然支持 这是 Spring AOP 基础,现在的用法也是基于历史的,只是更简便了 Pointcut ...
- 【python练习题】程序8
#题目:输出 9*9 乘法口诀表. for i in range(1,10): k = '' for j in range(1,i+1): k += '%s * %s = %s '%(i,j,i*j) ...