题目

一棵树,\(n\)个非叶子节点,编号为\(1-n\),\(m\)个叶子节点,编号为\(n+1-n+m\)

每条边有边权,修改边权的代价为\(|a-b|\) ;

定义一个叶子的距离为到1(根节点)的边的长度之和;

求最小的修改代价使得最后所有叶子的距离相同;

$1 \le M \le 300000 $

题解

​ (做完这个题之后有一种为什么uoj的好评只能点一次的遗憾)

  • 设\(f(x)\)表示最终距离为\(x\)的答案,对于叶子是下凸的并且每段都是一次函数,归纳都满足这个性质的并设最值的区间为\([L,R]\) ,注意到这一段的\(f'_u\)为0,左边<=-1,右边>=1,考虑转移:

    \[f_u(x) = \sum_{v} min_{k=0}^x \{ f_v(x-k) + |w-k| \} \\
    考虑单个的v:\\
    f_u(x) = \begin{cases}
    f_v(x) + w & x \le L \\
    f_v(L) + w-(x-L) & L \lt x \le L+w \\
    f_v(L) & L+w \lt x \le R+w \\
    f_v(R) + (x-R)-w & R+w \lt x \\
    \end{cases}
    \\也即把[0,L]向上平移w个单位,[L,R]向右移动w个单位;
    \\中间用斜率为-1的线连接,再从R+w作一条斜率为1的射线;
    \\所有v相加仍然是凸的
    \]

    这里有详细的说明,不过建议自己脑补一下

  • 斜率是连续的,设交点横坐标分别为\(x_m,x_m-1,\cdots,x_1,\cdots\), $ x_i $ 的左边斜率 $ =-i $ ,规定\(x_{m+1}\)为\(0\)

    \[ans = f_1(0) + \sum_{i=1}^{m}-i(x_i-x_{i+1}) = f_1(0) + \sum_{i=1}^{m} -x_i \\
    f_1(0)=\sum w
    \]

  • 维护交点,所有\(f_v\)相加即交点集合合并,可以知道此时最大的斜率为\(|v|-1\)直接再删除那么多个交点即可;

  • 用可并堆维护,我写的左偏树;

    #include<bits/stdc++.h>
    #define ll long long
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    using namespace std;
    const int N=600010;
    int n,m,ls[N],rs[N],rt[N],sz,ds[N];
    ll wv[N];
    typedef pair<int,int>pii;
    vector<pii>g[N];
    bool cmp(int a,int b){return wv[a]==wv[b]?a<b:wv[a]<wv[b];}
    char gc(){
    static char*p1,*p2,s[1000000];
    if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
    return(p1==p2)?EOF:*p1++;
    }
    int rd(){
    int x=0;char c=gc();
    while(c<'0'||c>'9')c=gc();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
    return x;
    }
    int merge(int x,int y){
    if(!x||!y)return x+y;
    if(cmp(x,y))swap(x,y);
    rs[x]=merge(rs[x],y);
    if(ds[ls[x]]<ds[rs[x]])swap(ls[x],rs[x]);
    ds[x]=ds[rs[x]]+1;
    return x;
    }
    int main(){
    // freopen("fireworks.in","r",stdin);
    // freopen("fireworks.out","w",stdout);
    ll ans=0;n=rd();m=rd();
    for(int i=2;i<=n+m;++i){
    int u=rd(),w=rd();
    g[u].pb(mk(i,w));
    ans+=w;
    }
    for(int i=n+1;i<=n+m;++i)rt[i]=++sz,rs[rt[i]]=++sz;
    for(int i=n;i;--i){
    int d=(int)g[i].size();
    for(int j=0;j<d;++j){
    int v=g[i][j].fi,w=g[i][j].se;
    wv[rt[v]]+=w;
    if(cmp(ls[rt[v]],rs[rt[v]]))wv[rs[rt[v]]]+=w;
    else wv[ls[rt[v]]]+=w;
    rt[i]=merge(rt[i],rt[v]);
    }
    for(int j=1;j<d;++j)rt[i]=merge(ls[rt[i]],rs[rt[i]]);
    }
    rt[1]=merge(ls[rt[1]],rs[rt[1]]);
    while(rt[1]){
    ans-=wv[rt[1]];
    rt[1]=merge(ls[rt[1]],rs[rt[1]]);
    }
    cout<<ans<<endl;
    return 0;
    }

【loj2568】【APIO2016】【学习笔记 左偏树】烟花表演的更多相关文章

  1. 左偏树 / 非旋转treap学习笔记

    背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...

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

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

  3. [luogu3377][左偏树(可并堆)]

    题目链接 思路 左偏树的模板题,参考左偏树学习笔记 对于这道题我是用一个并查集维护出了哪些点是在同一棵树上,也可以直接log的往上跳寻找根节点 代码 #include<cstdio> #i ...

  4. POJ3016-K-Monotonic(左偏树+DP)

    我觉得我要改一下签名了……怎么会有窝这么啰嗦的人呢? 做这题需要先学习左偏树<左偏树的特点及其应用> 然后做一下POJ3666,这题的简单版. 思路: 考虑一下维护中位数的过程原数组为A, ...

  5. 左偏树初步 bzoj2809 & bzoj4003

    看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...

  6. [JLOI2015]城池攻占 左偏树

    题目描述 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池.这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi &l ...

  7. 浅谈左偏树在OI中的应用

    Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...

  8. 【左偏树】【P3261】 [JLOI2015]城池攻占

    Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池.这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其 ...

  9. hdu 1512 Monkey King 左偏树

    题目链接:HDU - 1512 Once in a forest, there lived N aggressive monkeys. At the beginning, they each does ...

随机推荐

  1. 两个div并排显示,当浏览器界面缩小时会出现换行

    解决:规定两个子div的父div的宽 <div id="showDataDiv" style="width: 1000px"> <div st ...

  2. 如何在Mybatis的xml文件调用java类的方法

    在mybatis的映射xml文件调用java类的方法:使用的是OGNL表达式,表达式格式为:${@prefix@methodName(传递参数名称)} 1.如下代码所示:方法必须为静态方法:以下我只是 ...

  3. 【转载】C#中使用decimal.TryParse方法将字符串转换为十进制decimal类型

    在C#编程过程中,将字符串string转换为decimal类型过程中,时常使用decimal.Parse方法,但decimal.Parse在无法转换的时候,会抛出程序异常,其实还有个decimal.T ...

  4. Linux下which、whereis、locate、find命令作用

    1 which 查看可执行文件的位置,也可以找到命令别名 2 whereis 查看文件的位置 3 locate 系统数据库查找文件位置,数据库大约每天更新一次 4 find 根据查找条件,搜寻硬盘查询 ...

  5. GridPanel列头带有复选框的列

    由于工作需要,封装了ExtJS4,GridPanel列头带有复选框的列, 代码如下: /** * 列头带有复选框的列 * */ Ext.define("org.pine.widget.Che ...

  6. unity 用LineRender画四边形并测面积

    作为一个菜鸡,这个高中数学题差不多废了我两个上午...好了,废话不多说,直接上代码... using System.Collections.Generic; using UnityEngine; pu ...

  7. 【RAC】 RAC For W2K8R2 安装--grid的安装(四)

    [RAC] RAC For W2K8R2 安装--grid的安装(四) 一.1  BLOG文档结构图 一.2  前言部分 一.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学 ...

  8. 【RAC】 RAC For W2K8R2 安装--操作系统环境配置 (二)

    [RAC] RAC For W2K8R2 安装--操作系统环境配置 (二) 一.1  BLOG文档结构图 一.2  前言部分 一.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可 ...

  9. 阿里播放器踩坑记录 进度条重构 video loadByUrl失效解决方案

    如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ 如果解决不了,可以在文末进群交流. 文档地址:https://player.alicdn.com/aliplayer/index. ...

  10. Linux 常见 RAID 及软 RAID 创建

    RAID可以大幅度的提高磁盘性能,以及可靠性,这么好的技术怎么能不掌握呢!此篇介绍一些常见RAID,及其在Linux上的软RAID创建方法. mdadm 创建软RAID mdadm -C -v /de ...