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


    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+5;
    char *buf=(char *)malloc(1<<15),*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=1;x=0;char ch=getc();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';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=2,x,y;i<=n+m;i++){
            read(fa[i]);read(dis[i]);
            son[fa[i]]++;sum+=dis[i];
        }
        for(int i=n+m;i>1;i--){
            ll L=0,R=0;
            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-1,tot));
            rt[fa[i]]=merge(rt[fa[i]],rt[i]);
        }
        while(son[1]--) rt[1]=pop(rt[1]);
        dfs(rt[1]);
        cout<<sum;
        return 0;
    }
  • 相关阅读:
    Go语言核心36讲(Go语言实战与应用九)学习笔记
    Go语言核心36讲(Go语言实战与应用六)学习笔记
    Go语言核心36讲(Go语言实战与应用二十一)学习笔记
    Go语言核心36讲(Go语言实战与应用十二)学习笔记
    Go语言核心36讲(Go语言实战与应用七)学习笔记
    Go语言核心36讲(Go语言实战与应用二十)学习笔记
    Go语言核心36讲(Go语言实战与应用二十四)学习笔记
    .NET 云原生架构师训练营(对象过程建模)学习笔记
    Go语言核心36讲(Go语言实战与应用二十二)学习笔记
    .NET 云原生架构师训练营(ASP .NET Core 整体概念推演)学习笔记
  • 原文地址:https://www.cnblogs.com/shenben/p/6735775.html
Copyright © 2020-2023  润新知