4585: [Apio2016]烟火表演

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 100  Solved: 66
[Submit][Status][Discuss]

Description

烟花表演是最引人注目的节日活动之一。在表演中,所有的烟花必须同时爆炸。为了确保安
全,烟花被安置在远离开关的位置上,通过一些导火索与开关相连。导火索的连接方式形成
一棵树,烟花是树叶,如[图1]所示。火花从开关出发,沿导火索移动。每当火花抵达一个分
叉点时,它会扩散到与之相连的所有导火索,继续燃烧。导火索燃烧的速度是一个固定常
数。[图1]展示了六枚烟花{E1,E2...E6 }的连线布局,以及每根导火索的长度。图中还标
注了当在时刻 从开关点燃火花时,每一发烟花的爆炸时间。
Hyunmin为烟花表演设计了导火索的连线布局。不幸的是,在他设计的布局中,烟花不一定
同时爆炸。我们希望修改一些导火索的长度,让所有烟花在同一时刻爆炸。例如,为了让[图
1]中的所有烟花在时刻 13爆炸,我们可以像[图2]中左边那样调整导火索长度。类似地,为
了让[图1]中的所有烟花在时刻 14爆炸,我们可以像[图2]中右边那样调整长度。
修改导火索长度的代价等于修改前后长度之差的绝对值。例如,将[图1]中布局修改为[图2]
左边布局的总代价为6 ,而将[图1]中布局修改为[图2]右边布局的总代价为 5.
导火索的长度可以被减为0 ,同时保持连通性不变。
给定一个导火索的连线布局,你需要编写一个程序,去调整导火索长度,让所有的烟花在同
一时刻爆炸,并使得代价最小。
 

Input

所有的输入均为正整数。令 N代表分叉点的数量, M代表烟花的数量。分叉点从1 到N 编

号,编号为1 的分叉点是开关。烟花从N+1 到 N+M编号。1<=N+M<=300,000
输入格式如下:
N M
P2 C2
P3 C3
...
Pn Cn
PN+1 CN+1
...
PN+m CN+M
其中Pi 满足 1<=Pi<i,代表和分叉点或烟花i 相连的分叉点。 Ci代表连接它们的导火索长
度( 1<=Ci<=10^9)。除开关外,每个分叉点和多于1 条导火索相连,而每发烟花恰好与 1条导
火索相连。

Output

输出调整导火索长度,让所有烟花同时爆炸,所需要的最小代价

Sample Input

4 6
1 5
2 5
2 8
3 3
3 2
3 3
2 9
4 4
4 3

Sample Output

5

HINT

 

Source

可并堆
  考虑一个点f(x)表示将这个点子树所有叶节点深度变成x的最小代价
  发现是个下凸函数,并且是线性的
  对于一个点,假设f(x=[L,R])取得最小值
  考虑加上到父亲的边权w
    if(x<=L) f(x)=f(x)+w
    if(L<x<=L+w) f(x)=f(L)+w-(x-L)
    if(L+w<x<=R+w) f(x)=f(L)
    if(x>R+w) f(x)=f(L)+x-R-w
  发现实际上是把第一段向上平移,中间加入斜率为-1,0,1的直线
  把多个合并起来,右边的斜率最大值就是其度数
  那么我们考虑它到父亲贡献时,把斜率为正的点都pop掉
  然后暴力添加拐点即可,每次只会加2个
  合并就直接用可并堆了,我写的左偏树
  最后取出1这个点的最小值时,我们是这样计算的
  把斜率>=0的都pop掉,提取1-L的所有点
  f(0)=∑树边权,然后斜率每次-1直到0
  那么从右往左减就可以了
  sum-=p[i]可以理解为把p[i]的斜率=-1的贡献算进去,然后前面所有直线斜率-=1
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=6e5+;
char *buf=(char *)malloc(<<),*is=buf,*it=buf;
#define getc() (is==it&&(it=(is=buf)+fread(buf,1,1<<15,stdin),is==it)?0:*is++)
ll sum,v[N];
int n,m,fa[N],son[N],dis[N];int tot,rt[N],l[N],r[N];
inline void read(int &x){
int f=;x=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
x*=f;
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(v[x]<v[y]) swap(x,y);
r[x]=merge(r[x],y);
swap(l[x],r[x]);
return x;
}
inline int pop(int x){
return merge(l[x],r[x]);
}
void dfs(int x){
if(!x) return ;
sum-=v[x];
dfs(l[x]);
dfs(r[x]);
}
int main(){
read(n);read(m);
for(int i=,x,y;i<=n+m;i++){
read(fa[i]);read(dis[i]);
son[fa[i]]++;sum+=dis[i];
}
for(int i=n+m;i>;i--){
ll L=,R=;
if(son[i]){
while(--son[i]) rt[i]=pop(rt[i]);
R=v[rt[i]];rt[i]=pop(rt[i]);
L=v[rt[i]];rt[i]=pop(rt[i]);
}
v[++tot]=L+dis[i];
v[++tot]=R+dis[i];
rt[i]=merge(rt[i],merge(tot-,tot));
rt[fa[i]]=merge(rt[fa[i]],rt[i]);
}
while(son[]--) rt[]=pop(rt[]);
dfs(rt[]);
cout<<sum;
return ;
}

最新文章

  1. Conversion Operators in OpenCascade
  2. PL/SQL Developer去掉启动时自动弹出的Logon弹出框方法
  3. u11-nav02
  4. Codevs1299 切水果
  5. Axure简介
  6. Linux修改用户组
  7. python 零散记录(七)(上) 面向对象 类 类的私有化
  8. eclipse format的时候如何让@param后不换行
  9. 怎样解决VS2013模块对于SAFESEH 映像是不安全的
  10. KASAN实现原理【转】
  11. [LeetCode] 15. 三数之和
  12. openssl RSA密钥格式PKCS1和PKCS8相互转换
  13. ACM_贪心法_queue_Fence Repair
  14. PHP Excel导入日期单元格处理
  15. mysql / sqlserver / oracle 常见数据库分页
  16. 20 约束 异常处理 MD5 日志
  17. node API buffer
  18. 在Delphi中获得唯一32位长字符串
  19. 深入理解ES6之函数
  20. Linux x86架构下ACPI PNP Hardware ID的识别机制

热门文章

  1. Linux下安装Redmine(项目管理软件)
  2. Userdata
  3. iOS边练边学--定时任务和HUD
  4. 关于Cocos2d-x中让主角运动的方法
  5. 用 #include &lt;filename.h&gt; 格式来引用标准库的头文件
  6. mysql -- 重装mysql失败的解决办法
  7. Android camera 竖直拍照 获取竖直方向照片
  8. 【c语言】推断一个数是奇偶数
  9. Ubuntu 14.04 安装R 环境
  10. 未能在当前目标框架中解析主引用“System.Net.Http”,它是一个框架程序集。“.NETFramework,Version=v4.0”。若要解决此问题,请移除引用“System.Net.Http”,或将应用程序的目标重新指向包含“System.Net.Http”的框架版本。 Zephyr.Web