bzoj 4585 烟火表演 - 动态规划 - 可并堆
当前正在考虑某个节点,设$f(x)$表示算上它到父节点的边,后将所有叶节点到它的父节点的距离改为$x$的最小代价。设$g(x)$表示将它所在的子树内的所有叶节点到它的距离改为$x$的最小代价,它和它父节点的边的边权为$w$。
对于一个点的各个子树之间互相独立,所以这个点的$g$函数相当于,它的各个子节点的$f$函数值的和。
$g(x) = \sum_{y\in son(x)}f_{y}(x)$
对于$f$函数,我们需要做决策:
$f(x) = \min_{0\leqslant y\leqslant x}\left \{ g(y) + \left | w - (x - y) \right | \right \}$
这等价于将每个位置$x$,考虑它前面位置的$g$函数值,和函数$h(y) = \left | w - (x - y) \right |$的和,然后取一个最小值作为$f(x)$。
于是就懵逼。值域可能很大,数组也开不下,所以怎么办呢?
考虑这个函数图像具有的性质。
首先考虑叶节点的$f$函数(它的$g$函数没有意义)。它是一条优美的绝对值函数的图像:
然后在考虑它的父节点,它的父节点的$g$函数将若干个这样的函数加在了一起。因为旧函数的导函数递增,新函数的导函数也等于旧函数导函数的和,所以新函数斜率递增。
而且这个函数图像非常特殊,每遇到一个拐点,导函数的值加1。
它的父节点的$g$函数可能会长成下面这个样子(这图画得很不标准):
它一定会出现平着的一段区间$[a, b]$。然后分类讨论一下它变换到$f$函数。
当$0\leqslant x \leqslant a$时,显然$f(x)$的决策点取$x$最优。
当$a < x \leqslant a + w$时,显然$f(x)$的决策点取$a$最优。
当$a + w < x \leqslant b + w$时,显然$f(x)$的最优决策点取$x - w$。
当$x > b + w$时,决策点取$b$。
所以整理一下式子不难得到:
$f(x) = \left\{\begin{matrix}g(x) + w \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (0\leqslant x \leqslant a)\\ g(a) + w - x + a \ (a < x \leqslant a + w)\\ g(x - w) \ \ \ \ \ \ (a + w < x \leqslant b + w)\\ g(b) + x - b - w \ \ \ \ \ \ \ (x > b + w)\end{matrix}\right.$
这有什么用呢?
考虑它的图像的变化:
它相当于将平的一段向右移动了$w$个单位,然后将$[0, a]$的函数图像向上平移了$w$个单位,中间空的一段补斜率为-1的线段。
然后把$[b, +\infty )$的图像变成斜率为1的射线。
这样新函数的图像也是一个满足刚刚提到的两点性质的下凸壳,不难证明所有非叶节点的$f, g$函数都满足这样的性质。
因此,我们考虑用某个数据结构来维护拐点集合。
所以我们可以平衡树 + 启发式合并来做这道题。于是这样就被卡掉了。
所以怎么办呢?实际上,我并不需要维护一个完全有序的序列,我只要支持:
- 弹掉最大的某几个。
- 支持插入元素
- 支持快速合并
因为被弹掉的元素一去不复返,可以暴力弹掉它们,因此可以想到可并堆。
这样时间复杂度降为$O((n + m)\log (n + m))$。
Code
- /**
- * bzoj
- * Problem#4585
- * Accepted
- * Time: 8736ms
- * Memory: 18872k
- */
- #include <iostream>
- #include <cassert>
- #include <cstdlib>
- #include <cstring>
- #include <cstdio>
- #ifndef WIN32
- #define Auto "%lld"
- #else
- #define Auto "%I64d"
- #endif
- using namespace std;
- typedef bool boolean;
- #define ll long long
- const int N = 6e5 + ;
- typedef class SkewNode {
- public:
- ll val;
- SkewNode *l, *r;
- SkewNode() { }
- }SkewNode;
- SkewNode pool[N];
- SkewNode* top = pool;
- SkewNode* newnode(int val) {
- top->val = val;
- return top++;
- }
- SkewNode* merge(SkewNode* a, SkewNode* b) {
- if (!a || !b) return (a) ? (a) : (b);
- if (a->val < b->val) swap(a, b);
- a->r = merge(a->r, b);
- swap(a->l, a->r);
- return a;
- }
- int n, m;
- int *fa, *cs, *ss, *ks;
- ll *bs;
- SkewNode** rs;
- inline void init() {
- scanf("%d%d", &n, &m);
- n += m;
- bs = new ll[(n + )];
- fa = new int[(n + )];
- cs = new int[(n + )];
- ss = new int[(n + )];
- ks = new int[(n + )];
- rs = new SkewNode*[(n + )];
- memset(bs, , sizeof(ll) * (n + ));
- memset(ks, , sizeof(int) * (n + ));
- memset(ss, , sizeof(int) * (n + ));
- memset(rs, , sizeof(SkewNode*) * (n + ));
- for (int i = ; i <= n; i++)
- scanf("%d%d", fa + i, cs + i);
- }
- inline void solve() {
- for (int i = n - m + ; i <= n; i++) {
- int f = fa[i];
- ss[f] += , ks[f] += , bs[f] += cs[i];
- rs[f] = merge(rs[f], newnode(cs[i]));
- rs[f] = merge(rs[f], newnode(cs[i]));
- }
- for (int i = n - m; i > ; i--) {
- while (ss[i] > ks[i] + ) ss[i]--, rs[i] = merge(rs[i]->l, rs[i]->r);
- SkewNode *a = rs[i], *b = rs[i] = merge(rs[i]->l, rs[i]->r);
- bs[i] += cs[i], a->val += cs[i], b->val += cs[i], a->l = a->r = NULL;
- rs[i] = merge(rs[i], a);
- int f = fa[i];
- bs[f] += bs[i], ks[f] += ks[i], ss[f] += ss[i];
- rs[f] = merge(rs[f], rs[i]);
- }
- while (ss[] > ks[]) ss[]--, rs[] = merge(rs[]->l, rs[]->r);
- ll res = bs[];
- while (rs[])
- res -= rs[]->val, rs[] = merge(rs[]->l, rs[]->r);
- printf(Auto"\n", res);
- }
- int main() {
- init();
- solve();
- return ;
- }
bzoj 4585 烟火表演 - 动态规划 - 可并堆的更多相关文章
- UOJ #205/BZOJ 4585 【APIO2016】Fireworks 可并堆+凸包优化Dp
4585: [Apio2016]烟火表演 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 115 Solved: 79[Submit][Status] ...
- bzoj 4585: [Apio2016]烟火表演【左偏树】
参考:https://blog.csdn.net/wxh010910/article/details/55806735 以下课件,可并堆部分写的左偏树 #include<iostream> ...
- bzoj 4767 两双手 - 动态规划 - 容斥原理
题目传送门 传送门I 传送门II 题目大意 一个无限大的棋盘上有一只马,设马在某个时刻的位置为$(x, y)$, 每次移动可以将马移动到$(x + A_x, y + A_y)$或者$(x + B_x, ...
- [APIO2016]烟火表演
题目描述 https://www.lydsy.com/JudgeOnline/problem.php?id=4585 题解 这题太神了. 我们可以先列出一个dp方程,dp[x][d]表示x节点到所有叶 ...
- bzoj 1095 Hide 捉迷藏 - 动态点分治 -堆
Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双 ...
- 【BZOJ 1129】[POI2008]Per 二叉堆
这个东西读完题之后,就能知道我们要逐位计算贡献.推一下式子,会发现,这一位的贡献,是当前剩余的数字形成的序列的总数,乘上所剩数字中小于s上这一位的数的个数与所剩数字的总数的比.所以我们维护“当前剩余的 ...
- Bzoj 1975: [Sdoi2010]魔法猪学院 dijkstra,堆,A*,K短路
1975: [Sdoi2010]魔法猪学院 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1357 Solved: 446[Submit][Statu ...
- Bzoj 1598: [Usaco2008 Mar]牛跑步 dijkstra,堆,K短路,A*
1598: [Usaco2008 Mar]牛跑步 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 427 Solved: 246[Submit][St ...
- BZOJ 2006 NOI2010 超级钢琴 划分树+堆
题目大意:给定一个序列.找到k个长度在[l,r]之间的序列.使得和最大 暴力O(n^2logn),肯定过不去 看到这题的第一眼我OTZ了一下午... 后来研究了非常久别人的题解才弄明确怎么回事...蒟 ...
随机推荐
- unity3d-解密加密数据
利用RijndaelManaged加密解码.需要导入命名空间 using System.Security.Cryptography; 一个很简单的例子,最终的结果 解密和加密类 public clas ...
- border的特殊用法
大家很容易在一些网页上看到二级菜单上有一个小的三角形,这个小三角型 除了可以使用图片或者使用iconfont写出来,还可以使用border写出来 这边简单的为大家举一个例子,希望对大家有用吧! css ...
- 安装FusionInsight
1.在华为平台上下载整体客户端,不建议下载单个组件客户端,后期关联测试还是要装上的. 2.下载后需要将服务器上的客户端拷贝到本地.打开xShell,新建会话,登陆本地虚拟机上的Linux系统(19 ...
- C\C++程序结束另外的进程
WinExec("taskkill /f /im center_flextrbo.exe",SW_HIDE);
- windows Server2012 IIS8.0配置安装完整教程
IIS8.0是windows Server2012自带的服务器管理系统,和以往不同,IIS8.0安装和操作都比较简单,界面很简洁,安装也很迅速.今天我们重点完整的演示下Internet Informa ...
- c#之正则表达式
一,C#正则表达式符号模式 字 符 描 述 \ 转义字符,将一个具有特殊功能的字符转义为一个普通字符,或反过来 ^ 匹配输入字符串的开始位置 $ 匹配输入字符串的结束位置 * 匹配前面的零次或多次的子 ...
- C++/Java线程之分
JAVA线程状态图 1.C++/windows中主线程结束,其他线程必然死亡(即使调用pthread_detach解除父子关系,主线程消亡时也会导致子线程被迫关闭). ----1.1 一个进程中可以有 ...
- html5 手机端 通讯录 touch 效果
不说那么多直接上代码. <html> <head> <meta http-equiv="Content-Type" content="tex ...
- mouseTracking
[1]mouseTracking 追踪鼠标的标志位 作用:保存窗口部件默认是否接收鼠标移动事件.此成员变量在QWidget类中. [2]Qt Assistant 解释 翻译如下: 这个属性保存部件窗口 ...
- 20165215 2017-2018-2 《Java程序设计》第3周学习总结
20165215 2017-2018-2 <Java程序设计>第3周学习总结 教材学习内容总结 编程语言历经面向机器语言.面向过程语言.面向对象语言三个发展阶段. 面向对象语言的三个特点: ...