首先不难看出一个暴力的 \(dp\) 解法,考虑令 \(dp_{i, j}\) 表示考虑完前 \(i\) 个矩形,第 \(i\) 个矩形左端点在 \(j\) 时所需要的最小花费。

不难有转移:

\[dp_{i, j} = \min\limits_{j - (r_{i - 1} \ - \ l_{i - 1} \ ) \le k \le j + (r_{i} - l_{i})}{dp_{i - 1, k}} + |j - l_i|
\]

继续考虑对上面这个 \(dp\) 进行优化,可以发现的是,这个 \(dp\) 本质上是在对之前的某个区域取 \(\min\) 然后再加上一个之和每个位置有关的绝对值函数。

看起来十分抽象,那么我们可以考虑把抽象问题具体化,将 \(dp_i\) 看作是一个关于 \(j\) 的函数,再来考虑一下转移。

从初始的 \(i = 1\) 开始考虑,不难发现 \(dp_1\) 就是 \(y = |j - l_1|\) 这样一个绝对值函数。

再来考虑 \(i = 2\) 的情况,可以发现前一步取 \(\min\) 本质上是将这个绝对值函数的左段向左平移了 \(r_i - l_i\),右段向右平移了 \(r_{i - 1} - l_{i - 1}\),中间用一段斜率为 \(0\) 的线段接起来。

再来考虑加入 \(|j - l_i|\) 这个绝对值函数,不难发现图像的变化需要分三种情况,但整体的变化方式都是确定的,即 :

  • \(l_i\) 左边的函数斜率整体 \(-1\) 右边的函数斜率整体 \(+1\),同时斜率变化的点的横坐标不变。

可以发现,这样变化后的图像一定还是一个下凸包。

那么就不难通过归纳证明每次转移的过程都是类似于 \(i = 2\) 的情况的。

同时,因为每一个函数 \(dp_i\) 的斜率最大最小值都不会超过 \(\pm |2i - 1|\),因此我们可以考虑直接维护这些斜率变化的拐点。

为了方便起见,如果在这个拐点斜率变化了 \(k\),我们就直接插 \(k\) 个这个点进去代表斜率变化了 \(k\) 个 \(1\)。

首先,因为左右两个函数的平移是不同的,因此我们要分开维护左右两边的断点。

并且,因为函数只是平移,因此我们只需要记录当前平移了多长的距离即可,下面着重来考虑加入一个绝对值函数的过程。

  • 当插入的绝对值函数零点在图像斜率为 \(0\) 的那段上时。

不难发现左边之前的拐点还是拐点,右边亦然。唯一变化的是左边右都会要在 \(l_i\) 处添加一个拐点。

  • 当插入的绝对值函数零点在图像斜率为 \(0\) 的那段左边时。

不难发现此时左边会在 \(l_i\) 处添加两个拐点,同时失去最靠右的拐点,右边会得到左边失去的哪个拐点。(注意这里的拐点实际上代表着斜率变化为 \(1\),这样就方便地处理掉左边做靠右的直线斜率不为 \(-1\) 的情况了)

因为需要取出最右边的端点,因此我们需要一个大根堆来维护左边的拐点。

  • 当插入的绝对值函数零点在图像斜率为 \(0\) 的那段右边时。

同理于上一种情况,用小根堆维护右边的拐点。

那么最终的答案是什么呢?

不难发现其实是最终 \(dp_n\) 斜率为 \(0\) 的那段函数对应的纵坐标。

但是我们并没有记录每一次的纵坐标,怎么办呢?

你会发现每次斜率为 \(0\) 的直线位置总是会向右或者向左变化,而这两次斜率为 \(0\) 的直线中会存在一个交点。

同时因为对于每一层而言,取斜率为 \(0\) 线段上的点总是最优的。

结合上面那条性质,每次选定一个斜率为 \(0\) 的线段做为这一层的 \(j\) 即可,然后计算答案只要计算往下一层斜率为 \(0\) 交点纵坐标的变化量即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 1e5 + 5;
int n, l, r, nL, nR, TL, TR, len, ans, lastlen;
priority_queue <int> L;
priority_queue <int, vector <int>, greater <int> > R;
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
signed main() {
n = read(), l = read(), r = read();
L.push(l), R.push(l), lastlen = r - l;
for (int i = 2; i <= n; ++i, lastlen = len) {
l = read(), r = read(), len = r - l, TL += len, TR += lastlen;
nL = L.top() - TL, nR = R.top() + TR;
if(l < nL) ans += nL - l, L.pop(), R.push(nL - TR), L.push(l + TL), L.push(l + TL);
else if(l > nR) ans += l - nR, R.pop(), L.push(nR + TL), R.push(l - TR), R.push(l - TR);
else L.push(l + TL), R.push(l - TR);
}
printf("%lld", ans);
return 0;
}

这种方法被称为 \(\rm set\) 维护函数拐点,经常用与维护一次分段函数的变化问题。

AT2347 [ARC070C] NarrowRectangles的更多相关文章

  1. [atARC070E]NarrowRectangles

    记$len_{i}=r_{i}-l_{i}$,即第$i$个区间的长度 用$f_{i,j}$表示前$i$个区间合法,第$i$个区间位于$[j,j+len_{i}]$的最小代价,暴力dp的时间复杂度为$o ...

  2. AtCoder 杂题训练

    前言: 因为要普及了,今年没一等就可以退役去学文化课了,所以暑假把历年noip普及组都刷了一遍,离noip还有50+天,想弄点强化训练什么的. 想了想,就这些天学文化课之余有空就把AtCoder之前那 ...

  3. AtCoder刷题记录

    构造题都是神仙题 /kk ARC066C Addition and Subtraction Hard 首先要发现两个性质: 加号右边不会有括号:显然,有括号也可以被删去,答案不变. \(op_i\)和 ...

  4. 【AtCoder】ARC070

    ARC070 C - Go Home 题目大意:一只袋鼠第i秒可以向左或向右跳i步或者不跳,问从0跳到x的最小时间 就是1,2,3,4...k总和超过x的最小的k,因为如果超过了x的那部分需要减掉的那 ...

随机推荐

  1. .NET 云原生架构师训练营(ASP .NET Core 整体概念推演)--学习笔记

    演化与完善整体概念 ASP .NET Core 整体概念推演 整体概念推演到具体的形式 ASP .NET Core 整体概念推演 ASP .NET Core 其实就是通过 web framework ...

  2. Java面向对象笔记 • 【第3章 继承与多态】

    全部章节   >>>> 本章目录 3.1 包 3.1.1 自定义包 3.1.2 包的导入 3.1.3 包的访问权限 3.1.4 实践练习 3.2 继承 3.2.1 继承概述 3 ...

  3. Ranger-AdminServer安装

    Ranger-AdminServer安装,对应的Ranger版本1.0.0. RangerAdmin安装依赖如下组件: mysql solr IP/机器名 安装软件 运行进程 zdh-245 Rang ...

  4. ARM微处理器的七种运行模式

    ARM微处理器的七种运行模式: 用户模式(usr):正常的程序执行状态 快速中断模式(fiq):用于处理快速中断,对高速数据传输或通道处理. 中断模式(irq):对一般情况下的中断进行处理. 管理模式 ...

  5. 三角网格上的寻路算法Part.2—A*算法

    背景 继上一篇三角网格Dijkstra寻路算法之后,本篇将继续介绍一种更加智能,更具效率的寻路算法-A*算法,本文将首先介绍该算法的思想原理,再通过对比来说明二者之间的相同与不同之处,然后采用类似Di ...

  6. python中的break 和continue的区别

    break语句可以在循环过程中直接退出循环,而continue语句可以提前结束本轮循环 break的例子如图,当遇到的n为偶数时,直接退出循环,所以打印的结果只有1. continue例子如下图,当遇 ...

  7. react中state与setstate的使用

    我们可以利用state来定义一些变量的初始值 //放在construcor里 this.state = { list: [1, 2, 3] } 要更改state里的值,注意要遵循react里immut ...

  8. Centos6.9虚拟机环境搭建

    原文链接:https://www.toutiao.com/i6481534700216123918/ 一.准备工具 VMware Workstation CentOS-6.9-x86_64-minim ...

  9. 揭秘 BPF map 前生今世

    揭秘 BPF map 前生今世 本文地址:https://www.ebpf.top/post/map_internal 1. 前言 众所周知,map 可用于内核 BPF 程序和用户应用程序之间实现双向 ...

  10. 贝塞尔曲线(面)二三维可视化(Three+d3)

    贝塞尔曲线(面)二三维可视化(Three+d3) 在学完 games101 几何后开始实践,可视化贝塞尔曲线 我想实现三维的贝塞尔曲线,用 threejs,但是 threejs 控制太麻烦了,因此,我 ...