APIO2016 烟火表演

news/2024/11/15 19:26:05/文章来源:https://www.cnblogs.com/FLY-lai/p/18417000

传送门

给定一棵树,带边权。\(1\) 的代价可以使某边权 \(\pm 1\)。求最小代价使从根到叶子距离都相等。
\(n\le 3\times 10^5,w_e\le 10^9\)


\(f_u(x)\) 表示 \(u\) 的子树内把 \(u\) 到叶子的距离都变成 \(x\) 的最小代价。\(F_u(x)\) 表示 \(u\) 的子树内把 \(fa[u]\) 到叶子的距离都变成 \(x\) 的最小代价。

写出转移方程。

\[f_v(x)=\sum_{c\in son(v)}F_c(x) \]

\[F_c(x)=\min_{d}\{|len-d|+_c(x-d)\} \]

其中 \(len\) 表示 \(c\)\(fa[c]\) 的边长度。

引理:\(f_v()\) 是下凸函数,\(F_c()\) 是下凸函数。

证明:

\(F_c\) 是下凸的,试证明 \(f_{fa[c]}\) 下凸。
\(f_{fa[c]}()=\sum F_c()\)。考虑二阶导数,\(F_c\) 的二阶导为正,则 \(f_{fa[c]}()\) 的二阶导等于 \(F_c()\) 二阶导求和,也是正的。所以 \(f_{fa[c]}()\) 是下凸的。

\(f_c\) 是下凸的,试证明 \(F_c\) 下凸。
\(g(d)=|d-len|\)

\[F_c(x)=\min\{g(0)+f_c(x),g(1)+f_c(x-1),\dots\} \]

我们把这些图像都画一下。

观察到 \(f(x-1)\) 相较于 \(f(x)\) 右移了一个单位长度;\(g(1)\) 相较于 \(g(0)\) (如果 \(len\ge 1\) 的话)向下移了一个单位长度。

所以 \(g(1)+f(x-1)\)\(g(0)+f(x)\) 向右下方移了 \(1\) 格(如果 \(len\ge 1\));容易想到,当 \(d>len\) 之后,\(g(d)+f(x-d)\)\(g(d-1)+f(x-d+1)\) 向右上方移了 \(1\) 格。

当取了 \(\min\) 之后(\(F_c\))是什么样的?

保留 \(g(0)+f(x)\) 最低点及其左侧的拐点 \(x_0\),然后从 \(x_0\sim x_0+len\) 都是一条斜率 \(-1\) 的直线,\(x_0+len\sim +\infty\) 都是一条斜率 \(+1\) 的直线。

显然这还是一个下凸函数。(其实这个证明很不严谨,感性理解吧)


基于证明,我们尝试维护 \(f_c\)\(F_c\) 的图像。因为是凸的,维护拐点以及最左侧的起始点即可。

第一个问题是用什么维护拐点。用一个可重集维护,每一个元素都记录一个拐点;当一个元素重复出现,比如出现了 \(x\) 次,表示这个拐点到上一个拐点的斜率,相比于上一个拐点到上上个拐点的斜率,增加了 \(x\)

最左侧的起始点,就是 \((0,f_c(0))\) 或者 \((0,F_c(0))\),容易求。

第二个问题是 \(F_{son}\rightarrow f_{fa}\) 时,拐点是如何变化的。
容易想到,只要把所有 \(son\)\(F\) 图像拐点,全部合并(merge)起来即可。注意到从 \(f_c\) 最后一个拐点到 \(+\infty\) 的斜率是 \(c\) 的儿子数。

第三个问题是 \(f_c\rightarrow F_c\) 时,拐点是如何变化的。
观察上面证明的图像变化。

  1. 把斜率 \(>0\) 的全部改成斜率 \(1\),也就是把斜率 \(>0\) 的拐点全部 pop 了。
  2. 把斜率 \(=0\) 的那一段,向右平移 \(len\) 格。在做完 \(1\) 后,最靠右的两个拐点一定是斜率 \(0\) 的那一段。把它们取出来,都 \(+len\) 再放回去即可。
  3. "斜率 \(=0\) 的那一段的左端点" 到 "它左侧的拐点" 的斜率改成 \(-1\)。其实在做 \(2\) 的时候就已经顺便做了。

到这里已经可以做了。但是我们可以再多观察一个性质,更加优美地写代码。
因为每个 \(F\) 相邻拐点的斜率都 \(\le 0\),所以当产生斜率 \(\ge 1\) 的直线,必然是两个 \(F\) 合并了,而且必然产生且只产生一个。
所以 \(f_c\rightarrow F_c\) 进行 1 的时候,要把斜率 \(>0\) 的拐点全部 pop 了。这种拐点的个数就是 \(c\) 的儿子个数 \(-1\)

维护拐点的集合,因为只会从右边删,用左偏树即可。

注意到我们其实并不需要同时维护 \(f\)\(F\) 的图像。对于一个结点,它的 \(f\) 只在更新 \(F\) 的时候起作用,\(F\) 只在更新 \(f\) 的时候起作用,所以我们只维护 \(F\) 的图像。拿一个结点的 \(F\) 更新父结点的 \(f\),然后舍弃这个结点的 \(F\)。等到处理父结点的时候,再把 \(f\) 变成 \(F\)。注意在处理 \(1\) 的时候,不要把 \(f_1\) 也弄成 \(F_1\) 了。

当我们拥有了 \(f_1\),找到 \(f_1\) 的图像最低点。其函数值就是答案。

但是这里还有一个方法,可以简化我们求函数值的过程。首先 \(f_1(0)\) 就是所有边权的和,非常简单。\(f_1(0)\) 减去 "斜率 \(=0\) 的那一段的左端点及它左侧的所有拐点" 的 \(x\) 坐标之和,就是最低点的函数值。

注意到拐点总个数是 \(O(n)\) 的,所以复杂度 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>using namespace std;
typedef long long ll;
const int N = 6e5 + 5;struct Node {ll val;int l, r, d;Node() {val = l = r = 0;d = -1;}Node(ll v) {val = v;l = r = d = 0;}
};
int sz = 0;
Node a[N];
int new_Node(ll v) { //新建一个权值为v的结点 a[++sz] = Node(v);return sz;
}
int mrg(int L, int R) { //合并以L,R为根的左偏树,返回最终根结点 if (L == 0 || R == 0)return L + R;if (a[L].val < a[R].val)swap(L, R);a[L].r = mrg(a[L].r, R);if (a[a[L].l].d < a[a[L].r].d)swap(a[L].l, a[L].r);a[L].d = a[a[L].r].d + 1;return L;
}
int pop(int x) { //弹出x,顺便返回新堆的根结点 return mrg(a[x].l, a[x].r);
}int rt[N]; //rt[i]记录结点i的左偏树的根 int n, m;
int p[N], c[N]; //父结点,到父结点的边权 
int sons[N] = {}; //儿子个数 int main() {cin >> n >> m;ll f10 = 0;for (int i = 2; i <= n + m; i++) {cin >> p[i] >> c[i];f10 += c[i];sons[p[i]]++;}for (int i = n + m; i >= 2; i--) {for (int j = 1; j < sons[i]; j++) //第一步:弹出所有斜率>0的 rt[i] = pop(rt[i]);//第二步:把斜率0的那一段向右平移len(c[i])ll R = a[rt[i]].val;rt[i] = pop(rt[i]);ll L = a[rt[i]].val;rt[i] = pop(rt[i]);rt[i] = mrg(rt[i], mrg(new_Node(L + c[i]), new_Node(R + c[i])));//第三步:右端改成斜率1的,已经完成//当前结点的F合并到父结点的frt[p[i]] = mrg(rt[p[i]], rt[i]);}//把f1的斜率>0的也pop了,这样堆顶就是最低点for (int j = 1; j < sons[1]; j++)rt[1] = pop(rt[1]);rt[1] = pop(rt[1]); //把slope=0的右端点pop了while (rt[1]) {f10 -= a[rt[1]].val;rt[1] = pop(rt[1]);}cout << f10 << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/798755.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

织梦dedecms使用weight排序无效怎么办

织梦CMS (DedeCMS) 中使用 weight 排序无效的问题,通常是因为程序内部的排序逻辑存在问题。根据之前提供的信息,这个问题在DedeCMS 5.7版本中存在,并且可以通过修改底层代码来解决。下面是解决此问题的一般步骤: 解决方法定位代码:首先,找到织梦CMS的 plus 目录下的 list…

算法与数据结构——哈希优化策略与算法选择

哈希优化策略 在算法题中,我们通常通过线性查找替换为哈希查找来降低算法的时间复杂度。我们借助一个算法题来加深理解。Question 给定一个整数数组nums和一个目标元素target,请在数组中搜索“和”为target的两个元素,并返回他们的数组索引。返回任意一个即可。线性查找: 以…

织梦DEDECMS怎么实现全站动态浏览

要实现DedeCMS(织梦CMS)全站动态浏览,可以通过以下步骤来进行配置:首页动态化:登录织梦CMS的后台管理系统。 导航到“核心”->“全局配置”。 在“站点设置”标签页中,找到“主页网址”设置,确保主页网址是动态的,例如 http://www.example.com/ 而不是静态的 http:/…

织梦dedecms怎么调用图片集中图片的注释

在DedeCMS中调用图片集中的图片及其注释,可以通过自定义函数或者利用已有的函数来实现。下面是一个基于已有资料的示例,展示如何调用图片集中的图片及其注释。 首先,你需要确保你的图片已经被正确地添加到了织梦CMS的图集功能中。然后,你可以使用自定义函数来获取这些图片及…

JavaScript:对组织值进行排序

要对组织值进行排序,你可以使用JavaScript中的数组排序方法 sort()。下面是一些示例代码,展示如何对不同类型的组织值进行排序: 示例 1:对数字数组进行排序const numbers = [5, 2, 9, 1, 5, 6];// 使用 sort() 方法进行升序排序 numbers.sort((a, b) => a - b);console.…

dedecms缩略图报错怎么办

当遇到DedeCMS(织梦CMS)中缩略图报错的问题时,可以尝试以下几种解决方法来定位和解决问题:检查图片路径:确保缩略图的路径是正确的,有时候图片路径错误会导致缩略图无法显示。检查图片文件:确保图片文件本身没有损坏,并且是服务器支持的格式(如 .jpg, .png, .gif 等)…

如何优雅地处理返回值

我们已经知道了如何优雅的校验传入的参数了,那么后端服务器如何实现把数据返回给前端呢? 返回格式 后端返回给前端我们一般用 JSON 体方式,定义如下: {#返回状态码code:string, #返回信息描述message:string,#返回值data:object }CODE 状态码 Code 返回状态码,一般是…

白云龙期货分析-第九讲

九种职业操盘手高级战法 出征准备买卖法V旗买卖法双旗合并买卖法青龙取水买卖法中长线可选60日均线水杯买卖法南辕北辙买卖法国内和国外趋势不一样 断剑买卖法大阴阳买卖法童子拜佛买卖法 下影线多于上影线,向上趋势能量

浮点数的比较

浮点数与"零值" 精度损失: 浮点值与实际值不等,可能偏大可能偏小,都属于精度损失验证浮点数是否存在精度损失验证浮点数的差值是否存在精度损失浮点数直接比较验证结论: 浮点数在进行比较时,绝对不能使用双等号==来进行比较. 浮点数本身有精度损失,进而导致结果可能…

白家强的第一次作业

一、自我介绍 大家好,我的名字是白家强,我来自河北邢台,我的家乡处于华北平原,在我的家乡有美丽的苞米地和一望无垠的大麦田。我现就读于浙江理工大学22级自动化(1)班,是一名大三的学生。在杭州上学的这两年中,遇到了许许多多的人,结识了许多好友,认识了很多老师,我…

Qt加载天地图离线api开发包/从官网趴地图js代码/费了九牛二虎之力终于搞定

一、前言说明 网上关于如何趴天地图离线api文件的文章,只有少量的两三篇,而且几乎没有说全和说对,搞得评论也是一片懵逼,这里不行那你不行,思路可以借鉴就是。索性花了点时间,自己研究了如何从官网一步步趴下来js文件,最终所有离线能使用的功能全部搞定,也根本不会有ht…