Loj #2568. 「APIO2016」烟花表演

题目描述

烟花表演是最引人注目的节日活动之一。在表演中,所有的烟花必须同时爆炸。为了确保安全,烟花被安置在远离开关的位置上,通过一些导火索与开关相连。导火索的连接方式形成一棵树,烟花是树叶,如图 1所示。火花从开关出发,沿导火索移动。每当火花抵达一个分叉点时,它会扩散到与之相连的所有导火索,继续燃烧。导火索燃烧的速度是一个固定常数。图 1展示了六枚烟花 \(\{E_1, E_2, \ldots, E_6 \}\) 的连线布局,以及每根导火索的长度。图中还标注了当在时刻 \(0\) 从开关点燃火花时,每一发烟花的爆炸时间。

图 1

Hyunmin 为烟花表演设计了导火索的连线布局。不幸的是,在他设计的布局中,烟花不一定同时爆炸。我们希望修改一些导火索的长度,让所有烟花在同一时刻爆炸。例如,为了让图 1中的所有烟花在时刻 \(13\) 爆炸,我们可以像图 2中左边那样调整导火索长度。类似地,为了让图 1中的所有烟花在时刻 \(14\) 爆炸,我们可以像图 2中右边那样调整长度。

图 2

修改导火索长度的代价等于修改前后长度之差的绝对值。例如,将图 1中布局修改为图 2,左边布局的总代价为 \(6\),而将图 1中布局修改为图 2右边布局的总代价为 \(5\)。

导火索的长度可以被减为 \(0\),同时保持连通性不变。

给定一个导火索的连线布局,你需要编写一个程序,去调整导火索长度,让所有的烟花在同一时刻爆炸,并使得代价最小。

输入格式

所有的输入均为正整数。令 \(N\) 代表分叉点的数量,\(M\) 代表烟花的数量。分叉点从 \(1\) 到 \(N\) 编号,编号为 \(1\) 的分叉点是开关。烟花从 \(N+1\) 到 \(N+M\) 编号。

输入格式如下:

\(N\:\:M\)

\(P_2\:\:C_2\)

\(P_3\:\:C_3\)

\(\ldots\)

\(P_N\:\:C_N\)

\(P_{N+1}\:\:C_{N+1}\)

\(\ldots\)

\(P_{N+M}\:\:C_{N+M}\)

其中 \(P_i\) 满足 \(1\le P_i<i\),代表和分叉点或烟花 \(i\) 相连的分叉点。\(C_i\) 代表连接它们的导火索长度 \((1\le C_i\le 10^9)\)。除开关外,每个分叉点和多于 \(1\) 条导火索相连,而每发烟花恰好与 \(1\) 条导火索相连。

数据范围与提示

子任务 1(7 分):\(N=1,1 \le M \le 100\)。

子任务 2(19 分):\(1 \le N+M \le 300\),且开关到任一烟花的距离不超过 \(300\)。

子任务 3(29 分):\(1 \le N+M \le 5000\)。

子任务 4(45 分):\(1 \le N+M \le 3\times 10^5\)。

\(\\\)

参考博客

设\(f_i(x)\)为使得\(i\)的子树中所有叶子到\(i\)距离为\(x\)所付出的代价。很明显\(f_i(x)\)是个下凸的函数,而且中间有一整段斜率为\(0\)的区间,假设为\([L,R]\)。我们先考虑已经得到了\(f_u(x)\),要加上\(u\)的父亲到\(u\)的那条边(长度为\(w\))后函数怎么变化。

\[f_{fa_u}(x)=
\begin{cases}
f_u(x)+w & x\leq L\\
f_u(L)+w-(x-L) & L\leq x\leq L+w\\
f_u(L) & L+w <x\leq R+w\\
f_u(R)+x-w-R & R+w<x
\end{cases}
\]

假设最终这条边的边权为\(w'\),最优策略就是尽量使得\(x-w'\),也就是所有叶子到\(u\)的距离尽量往\([L,R]\)靠。

我们观察这个转移,相当于将\(L\)以左的部分抬高\(w\),然后接一条斜率为\(-1\)的直线,然后接一条斜率为\(0\)的直线,最后接斜率为\(1\)的直线。也就是说先将右端的斜率大于\(0\)的直线全部删除,再加入上述三条直线。

假设我们已经完成了这个操作,于是就把这个函数加到\(fa_u\)的函数中。我们发现累加函数会使斜率逐渐增大。比如函数\(1\)在\(x\geq x_1\)的部分斜率为\(1\),函数\(2\)在\(x\geq x_2\)的部分斜率为\(1\)(\(x_1<x_2\)),那么新函数在\(x_1\leq x\leq x_2\)的部分斜率为\(1\),在\(x>x_2\)的部分斜率为\(2\)。

所以我们只需要维护函数的拐点的横坐标就行了。考虑每合并一个儿子,函数的最大斜率都会\(+1\),所以函数最右端斜率\(>0\)的端点个数就是儿子的数量。具体实现可以用可并堆。

最后考虑得到最终的函数后怎么算答案。明显答案就在那一段斜率为\(0\)的区间,但是我们只维护了横坐标。我们知道\(f_1(0)=sum\),其中\(sum\)表示所有的边长度之和。每次合并函数,最大斜率\(+1\),同理最小斜率也会\(-1\),所有函数左侧的斜率也是递减的。假设最左侧斜率\(<0\)的一堆端点有\(k\)个,分别为\(x_1,x_2,\ldots,x_k\),那么答案就

\[sum-x_1*k+\sum_{i=2}^k(x_i-x_{i-1})*(k-i+1)\\
=sum-\sum_{i=1}^kx_i
\]

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 600005 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n,m;
struct tree {
int ls,rs,key;
ll val;
}tr[N<<1];
int Merge(int a,int b) {
if(!a||!b) return a+b;
if(tr[a].val<tr[b].val) swap(a,b);
tr[a].rs=Merge(tr[a].rs,b);
if(tr[tr[a].ls].key<tr[tr[a].rs].key) swap(tr[a].ls,tr[a].rs);
tr[a].key=tr[a].rs?tr[tr[a].rs].key+1:0;
return a;
}
int Pop(int a) {return Merge(tr[a].ls,tr[a].rs);} int fa[N];
int len[N];
ll sum;
int sn[N];
int rt[N];
int tot;
int main() {
n=Get(),m=Get();
for(int i=2;i<=n+m;i++) {
fa[i]=Get(),len[i]=Get();
sum+=len[i];
sn[fa[i]]++;
}
for(int i=n+m;i>=2;i--) {
ll l=0,r=0;
if(i<=n) {
while(--sn[i]) rt[i]=Pop(rt[i]);
r=tr[rt[i]].val;rt[i]=Pop(rt[i]);
l=tr[rt[i]].val;rt[i]=Pop(rt[i]);
}
tr[++tot].val=l+len[i];
tr[++tot].val=r+len[i];
rt[i]=Merge(rt[i],Merge(tot-1,tot));
rt[fa[i]]=Merge(rt[fa[i]],rt[i]);
} while(sn[1]--) rt[1]=Pop(rt[1]);
while(rt[1]) {
sum-=tr[rt[1]].val;
rt[1]=Pop(rt[1]);
}
cout<<sum;
return 0;
}

Loj #2568. 「APIO2016」烟花表演的更多相关文章

  1. 【LOJ】#2568. 「APIO2016」烟花表演

    题解 这个听起来很毒瘤的想法写起来却非常休闲,理解起来可能很费劲 例如,我们首先到猜到答案是个下凸包 然后是不是要三分???然而并不是orz 我们通过归纳证明这个下凸包的结论来总结出了一个算法 也就是 ...

  2. 「APIO2016」烟花表演

    「APIO2016」烟花表演 解题思路 又是一道 solpe trick 题,观察出图像变化后不找一些性质还是挺难做的. 首先令 \(dp[u][i]\) 为节点 \(u\) 极其子树所有叶子到 \( ...

  3. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  4. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  5. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  6. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  7. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  8. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  9. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

随机推荐

  1. Linux系统管理图文详解超详细精心整理

    前言:带你遨游于linux系统管理知识的海洋,沐浴春日里的阳光,循序渐进,看完之后收获满满. 本次讲解基于linux(centos6.5)虚拟机做的测试,centos7估计以后有时间再更新啊. lin ...

  2. 程序员,你还不会合理选择Filter、Interceptor、Aspect?

    小伙伴们应该听说过过滤器.拦截器.切面,印象上都能够起到截断拦截的作用,在做一些业务需求时,不知道如何选择,今天老顾就来介绍一下他们之间的区别. 过滤器可以拦截到方法的请求和响应 (ServletRe ...

  3. pytest框架之rerunfailures失败重运行机制

    web自动化测试中,稳定性在整个测试运行中都至关重要,但不能保证测试脚本或者测试环境一直都能够稳定,它牵扯到诸多因素,在这里就不赘述,pytest框架相较于unittest的一大优势就在于拥有用例失败 ...

  4. Map拼接URL地址

    import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * @Author: hoje * Des ...

  5. CodeForces 200D Programming Language

    Recently, Valery have come across an entirely new programming language. Most of all the language att ...

  6. python多进程multiprocessing Pool相关问题

    python多进程想必大部分人都用到过,可以充分利用多核CPU让代码效率更高效. 我们看看multiprocessing.pool.Pool.map的官方用法 map(func, iterable[, ...

  7. 初始HTML_表格

    Html表格 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...

  8. flex弹性布局没有生效

    display: -webkit-flex; /* 新版本语法: Chrome 21+ */ display: -webkit-box; /* 老版本语法: Safari, iOS, Android ...

  9. MIME格式解析

    - 邮件例子 一个MIME格式的邮件例子如下: Return-Path: <mlemos@acm.org> To: Manuel Lemos <mlemos@linux.local& ...

  10. HBase安装学习

    一.下载安装 $ wget http://archive.cloudera.com/cdh5/cdh/5/hbase-1.2.0-cdh5.7.0.tar.gz $ tar -zxvf hbase-1 ...