CF 713C Sonya and Problem Wihtout a Legend

2023-05-16

文章目录

          • 传送门
          • 题目大意
          • 正解
          • 通过维护关键点来维护信息
          • 参考代码

传送门
题目大意

  给定一个长度为 n ( n ≤ 3000 ) n \pod {n \le 3000} n(n3000) 的数组,可以花费 1 1 1 的代价将任意元素加 1 1 1 或者减 1 1 1。问将整个数组变得单调递增的最小代价。

正解

  出题人的正解是 O ( n 2 ) O(n^2) O(n2) 的动态规划。这里有 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的做法(来源):

  我们先用一个套路把严格单调递增变成非严格单调递增:只需要令 a i = a i − i a_i = a_i - i ai=aii 即可。设 f i ( x ) f_i(x) fi(x) 表示使得前 i i i 个数满足单调不递减的条件并且第 i i i 个数不超过为 x x x 的最小代价,有状态转移方程:

f i ( x ) = { min ⁡ y ≤ x { ∣ a i − Y ∣ } i = 1 min ⁡ y ≤ x { f i − 1 ( Y ) + ∣ a i − Y ∣ } otherwise f_i(x) = \begin{cases} \min_{y \le x} \{ |a_i - Y| \} & i = 1 \\ \min_{y \le x} \{ f_{i - 1}(Y) + |a_i - Y| \} & \text{otherwise} \end{cases} fi(x)={minyx{aiY}minyx{fi1(Y)+aiY}i=1otherwise

  可以发现, f i ( x ) f_i(x) fi(x) 是一个单峰函数:感性理解下,当 x x x 很小时,要把前面所有数都变得很小,代价就很大,当 x x x 很大时,把 a i a_i ai 变成 x x x 的代价也越来越多。

  我们设当 f i ( x ) f_i(x) fi(x) 取得最小值的 x x x x i x_i xi。显然可以根据 x i x_i xi 的大小分为两种情况:

  1. x i − 1 ≤ a i x_{i - 1} \le a_i xi1ai,那么显然有 x i = a i x_i = a_i xi=ai(既然不动都能满足条件了,那就不动吧);
  2. x i − 1 > a i x_{i - 1} > a_i xi1>ai,好像我们没法分析了???不如我们用图像来解决这个问题。

  我们设直角坐标系 x O y xOy xOy,其中 y = f i ( x ) y = f_i(x) y=fi(x)。我们仔细分析下这个图应该长什么样的:

(图为示意图)

  斜率为 0 0 0 的直线的前面的直线的斜率一定递减,为什么呢?因为随着我们 x x x 的减小,前面需要修改的数也就越来越多。而斜率为 0 0 0 的直线的后面只有一条斜率为 1 1 1 的直线,相当于我们只修改了 a i a_i ai

  我们考虑转移的意义。对于在后面新加入的一个数 a i a_i ai a i a_i ai 对总代价的贡献显然为 y = ∣ x i − a i ∣ y = |x_i - a_i| y=xiai

  而转移后的函数图像显然是状态转移方程的图像。


  上图对应情况 1 1 1,即 a i ≥ x i a_i \ge x_i aixi x i x_i xi 为斜率为 0 0 0 的最左点),那么显然,转移后,图像的最低点为 x = a i x = a_i x=ai 处。因此我们有结论 x i = a i x_i = a_i xi=ai。另外,很显然的是,对于原函数图像我们不需要斜率大于 0 0 0 的部分,因为这样转移一定不优。

  下面让我们看看如何分析情况 2 2 2

  观察发现,我们无论如何也不可能选择让 a i a_i ai 变小(不管是从逻辑上还是从图形上都很显然),继续观察发现,当 x = a i − 1 x = a_{i - 1} x=ai1 时, f i ( x ) f_i(x) fi(x) 有最小值 f i − 1 ( x i − 1 ) + ( x i − 1 − a i ) f_{i - 1}(x_{i - 1}) + (x_{i - 1} - a_i) fi1(xi1)+(xi1ai)


  显然的是, f x n f_{x_n} fxn 就是原问题的答案。如果我们能够维护函数图像,问题就解决了。

通过维护关键点来维护信息

  根据前面的分析,我们有一个新的状态转移方程:

f i ( x i ) = { f i − 1 ( x i − 1 ) a i ≥ x i − 1 f i − 1 ( x i − 1 ) + ( x i − 1 − a i ) a i < x i − 1 f_i(x_i) = \begin{cases} f_{i - 1}(x_{i - 1}) & a_i \ge x_{i - 1} \\ f_{i - 1}(x_{i - 1}) + (x_{i - 1} - a_i) & a_i < x_{i - 1} \end{cases} fi(xi)={fi1(xi1)fi1(xi1)+(xi1ai)aixi1ai<xi1

  换句话说,我们的答案为 x i − 1 − a i x_{i - 1} - a_i xi1ai 的和。也就是说我们可以仅仅维护 x i − 1 x_{i - 1} xi1 就达到计算答案的目的。

  前面,我们仅仅解决了最优解在哪里的问题,那么剩下部分的图像是怎样的呢?

  当 a i ≥ x i − 1 a_i \ge x_{i - 1} aixi1 时,显然,将不会存在斜率为 0 0 0 的直线。每一个关键点( x = x i x = x_i x=xi 的点为关键点)的斜率都会加 1 1 1。(想一想,为什么)

  当 a i < x i − 1 a_i < x_{i - 1} ai<xi1 时, a i a_i ai 之后的直线的斜率减 1 1 1,之前的斜率加 1 1 1,因此仍然满足斜率递增的性质,但是由于后面的斜率会加 1 1 1,一定会导致恰好一条斜率为 1 1 1 的直线出现,我们删除它即可。细节留给你们思考。

  用大根堆来维护关键点的横坐标。具体实现时,我们不需要保存斜率为 0 0 0 的直线,也就是说要把前面我说的大于 0 0 0 改成大于等于 0 0 0

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
	INT_PUT a = 0; bool positive = true;
	char ch = getchar();
	while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
	if (ch == '-') { positive = false; ch = getchar(); }
	while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
	return positive ? -a : a;
}
void printOut(INT_PUT x)
{
	char buffer[20]; int length = 0;
	if (x < 0) putchar('-'); else x = -x;
	do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
	do putchar(buffer[--length]); while (length);
	putchar('\n');
}

const int maxn = 3005;
int n;
int a[maxn];

void run()
{
	n = readIn();
	for (int i = 1; i <= n; i++)
		a[i] = readIn();

	LL ans = 0;

	std::priority_queue<int> q;
	for (int i = 1; i <= n; i++)
	{
		int& x = a[i];
		x -= i;
		q.push(x);
		if (q.size() && q.top() > x)
		{
			ans += q.top() - x;
			q.pop();
			q.push(x);
		}
	}
	printOut(ans);
}

int main()
{
	run();
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CF 713C Sonya and Problem Wihtout a Legend 的相关文章

随机推荐

  • LaTeX 005:删除一个命令

    C 43 43 的预编译系统中 xff0c 可以使用 undef 来取消 xff08 Undefine xff09 一个宏 LaTeX LaTeX L A T E X 中是否有类似于这样的功能呢 xff1f 网上的一个评论给出了解决方案 x
  • LaTeX 006:添加一个只有文字没有标号的脚标

    想要如下的效果 xff1a 该脚标只想要写一句注释 xff0c 没有对应的正文文本 如何实现 xff1f 直接使用 footnotetext 是不佳的 xff0c 前面会加上当前的脚标标号 xff0c 而且该标号不会自动递增 虽然 foot
  • GetExitCodeProcess 所需运行时间

    GetExitCodeProcess 在 Windows 中用于获取进程的返回代码 这个看上去只有一个读操作的函数运行速度如何呢 xff1f 直觉上不会很快 xff0c 因为它肯定涉及操作系统进程表的操作 下面做了一个实验 代码 xff1a
  • C++ 继承时返回值或参数的类型是父类的行为

    见以下代码的 base t amp operator 61 const base t amp another 还没有搞清楚 span class token macro property span class token directive
  • LaTeX 007:texify 调用 zhmakeindex

    如文档所述 xff0c 在系统增加一个值为 zhmakeindex 路径的环境变量 MAKEINDEX 即可
  • 转载:LaTeX 定义参数变长的命令

    本文作者 xff1a Liam Huang 本文链接 xff1a https liam page 2017 07 30 define a new command with different amount of parameters in
  • 一个简单的 Lex 词法分析程序示例

    作为一个学习 Lex 词法分析程序的例子 xff0c 下面的 lex 程序将会生成一个分析 LaTeX 中命令的词法分析器 下面的程序包含了很多 lex 语言的语法 xff0c 正则表达式除外 正则表达式的用法网上比较多 xff0c 这里不
  • mysql数据库conf配置详解

    mysqld port 61 6033 skip grant tables datadir 61 usr tools mysql data socket 61 usr tools mysql mysql sock user 61 mysql
  • LaTeX 008:比较方便的键入下划线的方式

    在 LaTeX 中 xff0c 我们有时会需要输入下划线 直接键入 是不行的 xff0c 会出现的编译错误 xff0c 正如网友所述 xff0c LaTeX 为了简化对编译错误的处理禁止在文本模式 xff08 text mode xff09
  • LaTeX 009:自定义带有 * 号的命令

    LaTeX 中 xff0c 我们经常见到 section 和 section xff0c 分别表示有编号的 section 和没有编号的 section 我们也想自己定义带有 号的命令 xff0c 但写下面的代码时却报错了 xff1a ne
  • 2022 New Year‘s Resolution

    Some Might Say 2022 New Year 39 s Resolution Some might say we are on the edge of the new era Always are they saying thi
  • C++ 多线程编程导论(中)

    受篇幅限制 xff0c 上半部分不再更新 xff0c 填坑的新内容都放在此文章中 文章目录 参考资料线程安全 xff08 续 xff09 互斥访问 互斥体 xff08 mutex xff09 和锁 xff08 lock xff09 什么是互
  • C++ 使用模板序列化/反序列化固定键值对

    仅是一个原型 xff0c 留作记录 我感觉可以写出非常逆天的代码 span class token macro property span class token directive hash span span class token d
  • 编译原理习题两则(龙书,写出语言的正则定义)

    3 3 5 3 注释 xff0c 即 和 之间的串 xff0c 且串中没有不在双引号 xff08 34 xff09 中的 注 xff1a 假设双引号是匹配的 思路 xff1a 从空串开始写 xff0c 写出整体框架后 xff0c 通过分类讨
  • 2023 New Year‘s Resolution

    This Is Game 2023 New Year 39 s Resolution My 2022 ended with a day of game I am convinced that I am not to blame becaus
  • 补录:2018 和 2019 New Year‘s Resolution

    前言 xff1a 吉光片羽 xff0c 以飨读者 2018 New Year 39 s Resolution One year and a half ago I felt that life in 2020 would be quite d
  • 原博文地址

    由于账号问题 xff0c 现更改为这个账号 xff0c 以下为原博文地址 使用WH MOUSE LL钩子来判断按键是否是mouse event模拟的 http blog csdn net qq 26140973 article detail
  • [NOI 2003] 文本编辑器 Splay 维护序列 / 块状链表

    传送门 xff08 JZOJ xff09 xff08 第一道全国决赛题 xff09 解法 1 xff1a 使用 Splay 维护 不管怎么说 xff0c 总和刚刚学过的迎合上了 这道题可以直接上 Splay 维护线性序列 xff0c 光标位
  • 一次macOS的升级填坑(macOS Catalina - macOS Monterey)

    目录 小序一 升级前操作二 升级中三 问题填坑1 像我一样长时间卡在一个进度条怎么办2 在更新途中重启过电脑 xff08 完整流程填坑 xff09 3 安装之后不能开机 xff0c 如何紧急拷贝资料4 安装不成功 xff0c 如何重新安装系
  • CF 713C Sonya and Problem Wihtout a Legend

    文章目录 传送门题目大意正解通过维护关键点来维护信息参考代码 传送门 题目大意 给定一个长度为 n n 3000