CHT 另解

news/2025/3/16 8:37:07/文章来源:https://www.cnblogs.com/Tom-tanjiaqi/p/18773248

引入

CHT 又叫 凸包优化,是一种利用一次函数(斜率)来优化 Dp 的一种方法。
它的独特之处在于,传统斜率优化依靠的是一个一个的点,而凸包优化是利用一条条直线来优化,省去了一些码量。
我们用一道例题引入。

例1 HDU-3480

Dp 暴力

Link
题目是说,将 \(n\) 个数划分到 \(m\) 个集合中,使得 \(Cost\) 最小。

\[Cost = \sum_{i=1}^{m}(\max_{x\in s_i} x - \min_{x\in s_i} x)^2 \]

显然应该给输入的 \(n\) 个数排序。
暴力 Dp,令 \(f_{i,j}\) 表示将 \([1,j]\) 的数放进 \(i\) 个集合里面的最小代价。
转移显然,\(f_{i,j}=\min (f_{i-1,k-1}+(a_j-a_k)^2)\) ,时间复杂度 \(O(n^2m)\)
我们来把式子拆开,看看能不能发现什么?

\[(a_j-a_k)^2+f_{i-1,k-1}=a_j^2+a_i^2-2\times a_j \times a_i+f_{i-1,k-1} \]

将关于 \(j\) 的项放在一起,得到:

\[(a_i^2)+[(-2 \times a_j) \times a_i+(f_{i-1,k-1}+a_j^2)] \]

\(k_j=-2\times a_j\)\(b_j=f_{i-1,k-1}+a_j^2\),则:

\[f_{i,j}=a_i^2+\min(k_j \times a_i+b_j) \]

后面的那一坨式子就是一个一次函数的解析式,这里可以用李超线段树,或者使用今天的凸包优化。

维护凸包

我们可以沿用斜率优化的思想,将他们维护成一个上凸包,直线斜率单调递减,维护出来如下图所示。
image
那么在查询答案和插入时又怎么办呢?

队头维护

我们将目前的直线压入队列,因为 \(a_i\) 单调递增,所以查询值从队头开始,反之,插入从队尾插入。
在什么情况下,对头的直线或队尾直线被弹出?继续往下看。
image
\(x\) (也就是 \(a_i\))位于绿色直线的时候,红色直线提供最大值。image
但是当 \(x\) (也就是 \(a_i\)) 逐渐变大时,红色在某一时刻不提供最小值了,于是就可以弹出红色直线。

队尾维护

image
当出现这样的情况(紫色为新插入直线)时,蓝色直线可以弹出,因为紫色的函数在与红色的交点以右的地方都比蓝色小,蓝色不可能提供最小值了。所以应当弹出。
image
反之,三条直线都有可能提供最小值,故保留。
话不多说,看代码:

#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1e4 + 5;
const double eps = 1e-8;
struct line {ll k, b;ll f(ll x) {return k * x + b;}double X(const line &o) {return 1.0 * (b - o.b) / (o.k - k);}
};
deque<line> st;
ll dp[2][N], sum[N], a[N];
int n, m;
void solve(int ncnt) {cin >> n >> m;for (int i = 1; i <= n; ++i) {cin >> a[i]; }sort(a + 1, a + n + 1);n = unique(a + 1, a + n + 1) - a - 1;for (int i = 1; i <= n; ++i) dp[0][i] = (a[i] - a[1]) * (a[i] - a[1]);for (int i = 2; i <= m; ++i) {st.clear(); st.push_back({0, 0});for (int j = 1; j <= n; ++j) {while (st.size() >= 2 && st[0].f(a[j]) > st[1].f(a[j])) st.pop_front();dp[1][j] = a[j] * a[j] + st.front().f(a[j]);line cur = {-2 * a[j + 1], a[j + 1] * a[j + 1] + dp[0][j]};while (st.size() >= 2 && st.back().X(st[st.size() - 2]) - st[st.size() - 2].X(cur) > eps) st.pop_back();st.push_back(cur);}for (int j = 1; j <= n; ++j) {dp[0][j] = dp[1][j];}}cout << "Case " << ncnt << ": " << dp[1][n] << '\n';
}
int main() {FASTIO;int t; cin >> t;int ncnt = 0;while (t --) solve(++ncnt);return 0;
}

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

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

相关文章

PHP 发送电子邮件 功能 用法运用 详解

PHP发送电子邮件功能、用法及运用详解 一、PHP发送电子邮件的基本概述 PHP提供了多种方式来发送电子邮件,其中最常用的方法是使用内置的mail()函数或通过SMTP(Simple Mail Transfer Protocol)协议。随着技术的发展,许多开发者更倾向于使用第三方库(如PHPMailer)来增强邮件…

Linux 离线安装 zstd

前言:本文操作是在 CentOS-7 下执行的,不确定在其他 Linux 发布版是否能同样正常执行。1、检查前置依赖组件在安装 zstd 之前,需要确认已安装了相关依赖组件: gcc 。 rpm -qa | grep gcc前置依赖组件的具体离线安装方法请参考:CentOS-7离线安装gcc 2、下载zstd安装包 官方…

HarmonyOS Next开发教程之地图定位

今天分享一下在鸿蒙开发中的地图定位问题,也就是在地图中如何定位自己所在的位置。 关于如何加载显示地图在之前的文章已经详细介绍过,有问题的友友可以点击查看: HarmonyOS NEXT实战教程-实现Keep运动轨迹 将地图定位到自己所在的位置,有几种方法: 一种是在初始化地图前先…

HarmonyOS Next实战教程:实现中间凹陷的异形tabbar

今天要和大家分享的实战案例是实现中间凹陷的tabar前些天在做墨迹天气的时候看到了这种异形的tabbar,看起来比较有挑战性,因为鸿蒙版的墨迹天气app还没有这个东西,我决定尝试做一下。 系统的Tabs肯定是不行了,我们需要自定义。 难度直接拉满,直接做最难的部分,就是这个中…

HarmonyOS NEXT实战:高仿墨迹天气开发手记(附源码)

老余说3月份的神秘产品是为纯血鸿蒙而生的一款全新形态的手机,别人想象不到的手机产品,这次的保密工作真是非常到位,让人十分期待。 闲言少叙,今天为大家分享新年的第一个实战项目,高仿墨迹天气 这个项目中有一些复杂的动效和曲线,对于新手友友来说可能会有一点难,不过没…

VSCode + CMake + MinGW 在 Windows 下的简易调试指南

VSCode + CMake + MinGW 在 Windows 下的简易调试指南 目录VSCode + CMake + MinGW 在 Windows 下的简易调试指南准备工作下载VSCode下载CMake下载MinGW待编译源码VSCode调试task.json 配置launch.json 配置开始调试鉴于网络上关于VSCode的调试的教程不多,并且掺杂着大量的随机…

指令集并行与开发进阶算法

进阶算法 基础算法无法解决中断恢复的问题,即假如有两个写寄存器的操作,指令1,指令2,可能乱序执行时指令2的结果已经将写回了寄存器,但是指令1还未执行,此时发生中断后,从指令1重新开始执行,就会重新进行两次写入,将会发生错误。 只要保证后面指令修改机器状态时, 前面…

指令集并行与开发Tomasulo算法

指令集并行与开发Tomasulo算法 1. 概念 Tomasulo 方法是一种用于在超标量处理器中执行指令并处理数据相关(数据相关性)的方法。它主要通过对指令进行乱序执行和动态调度来提高指令级并行性。 可以通过寄存器重命名消除 WAR 和 WAW 相关(通过保留站号间接实现重命名) 也可以…

芯片存储器层次结构概述

存储器层次结构概述 1. Cache的作用 Cache结构与作用,如图2-5所示。图2-5 Cache结构与作用 介绍一下Cache具有特征。Cache没有程序上的意义,只是为了降低访存延迟;处理器访问Cache和访问存储器使用相同的地址。 Tag存储cache块在主存中的首地址(cache每个字节都给一个地址太…

推荐1《AI芯片开发核心技术详解》、2《智能汽车传感器:原理设计应用》、3《TVM编译器原理与实践》、4《LLVM编译器原理与实践》书,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

MYSQL-DDL操作

点击查看代码 ```plaintext create table tb(id int comment ID,username varchar(20) comment 用户名,name varchar(10) comment 姓名,age int comment 年龄,gender char(1) comment 性别 )comment user测试表</details> ![](https://img2024.cnblogs.com/blog/3619156…

JetBrains IDEA破解后一直跳出激活弹窗

正文 一直跳弹窗是因为选了区域中国,你可以断网,然后到打开设置,搜索区域,选择亚洲。保险起见,保存后先关闭idea,再连接网络,启动IDEA。