洛谷 P3648 APIO2014 序列分割 题解

写了挺多斜率优化的题目了,这道(差点)就速切了,原因还是单调队列维护斜率的写法出锅。

题意

题目描述

你正在玩一个关于长度为 n n n 的非负整数序列的游戏。这个游戏中你需要把序列分成 k + 1 k + 1 k+1 个非空的块。为了得到 k + 1 k + 1 k+1 块,你需要重复下面的操作 k k k 次:

选择一个有超过一个元素的块(初始时你只有一块,即整个序列)

选择两个相邻元素把这个块从中间分开,得到两个非空的块。

每次操作后你将获得那两个新产生的块的元素和的乘积的分数。你想要最大化最后的总得分,并知道得到最大总分的方案。

如果有多种方案使得总得分最大,输出任意一种方案即可。

输入

7 3
4 1 3 4 0 2 3

输出

108
1 3 5

2 ≤ n ≤ 100000 , 1 ≤ k ≤ min ⁡ { n − 1 , 200 } 2 \leq n \leq 100000, 1 \leq k \leq \min\{n - 1, 200\} 2n100000,1kmin{n1,200}

思路

对序列分割,分割的顺序会不会对答案造成影响呢?

之前 smsky 选拔的时候,第一题与之是类似的。我们可以猜测:对于这种大块的乘积,如果固定了分割点,无论分割顺序如何,都不会改变结果

考虑简要地证明这个结论:设序列有三个分割点 1 , 2 , 3 1,2,3 1,2,3,序列被分割成了元素和依次为 a , b , c , d a,b,c,d a,b,c,d 的四块。

  • 1 , 2 , 3 1,2,3 1,2,3 的顺序分割: a n s 1 = a ( b + c + d ) + a ( b + c ) + c d = a b + a c + a d + b c + b d + c d ans_1=a(b+c+d)+a(b+c)+cd=ab+ac+ad+bc+bd+cd ans1=a(b+c+d)+a(b+c)+cd=ab+ac+ad+bc+bd+cd
  • 3 , 1 , 2 3,1,2 3,1,2 的顺序分割: a n s 2 = ( a + b + c ) d + a ( b + c ) + b c = a d + b d + c d + a b + a c + b c ans_2=(a+b+c)d+a(b+c)+bc=ad+bd+cd+ab+ac+bc ans2=(a+b+c)d+a(b+c)+bc=ad+bd+cd+ab+ac+bc;
  • 1 , 3 , 2 1,3,2 1,3,2 的顺序分割:。。。

多枚举几组,我们发现 a n s ans ans 始终不变。

因此简证成立。

那么我们就可以 1 ∼ n 1\sim n 1n 枚举分割点了。考虑朴素的 dp,设 f i , t f_{i,t} fi,t 表示前 i i i 个数,分割了 t t t 次,且在 i i i 之后不分割的最小花费。(这样子就不用特意讨论 n n n 之后还被分割了的情况了)

处理前缀和 p r e i = ∑ j = 1 i a j pre_i=\sum_{j=1}^{i}a_j prei=j=1iaj。枚举分割点 j j j,在 1 ∼ i 1\sim i 1i 分成了 1 ∼ j 1\sim j 1j j + 1 ∼ i j+1\sim i j+1i 两块,和分别是:
f i , t = max ⁡ j = 1 i − 1 { f j , t − 1 + p r e j ( p r e i − p r e j ) } f_{i,t}=\max_{j=1}^{i-1}\left\{f_{j,t-1}+pre_j(pre_i-pre_j)\right\} fi,t=j=1maxi1{fj,t1+prej(preiprej)}

设两个决策点 j 1 < j 2 j1<j2 j1<j2 j 2 j2 j2 优于 j 1 j1 j1,那么:
f j 1 , t − 1 + p r e j 1 ( p r e i − p r e j 1 ) < f j 2 , t − 1 + p r e j 2 ( p r e i − p r e j 2 ) f_{j1,t-1}+pre_{j1}(pre_i-pre_{j1})<f_{j2,t-1}+pre_{j2}(pre_i-pre_{j2}) fj1,t1+prej1(preiprej1)<fj2,t1+prej2(preiprej2)

f j 1 , t − 1 + p r e i p r e j 1 − p r e j 1 2 ) < f j 2 , t − 1 + p r e i p r e j 2 − p r e j 2 2 f_{j1,t-1}+pre_ipre_{j1}-pre_{j1}^2)<f_{j2,t-1}+pre_ipre_{j2}-pre_{j2}^2 fj1,t1+preiprej1prej12)<fj2,t1+preiprej2prej22

f j 1 , t − 1 − f j 2 , t − 1 + p r e j 2 2 − p r e j 1 2 < p r e i ( p r e j 2 − p r e j 1 ) f_{j1,t-1}-f_{j2,t-1}+pre_{j2}^2-pre_{j1}^2<pre_i(pre_{j2}-pre_{j1}) fj1,t1fj2,t1+prej22prej12<prei(prej2prej1)

注意到 p r e j 2 − p r e j 1 ≥ 0 pre_{j2}-pre_{j1}\ge 0 prej2prej10,依旧注意避开 0 0 0;如果非 0 0 0 就可以直接除过去:
f j 1 , t − 1 − f j 2 , t − 1 + p r e j 2 2 − p r e j 1 2 p r e j 2 − p r e j 1 < p r e i \frac{f_{j1,t-1}-f_{j2,t-1}+pre_{j2}^2-pre_{j1}^2}{pre_{j2}-pre_{j1}}<pre_i prej2prej1fj1,t1fj2,t1+prej22prej12<prei

s l o p e < p r e i slope<pre_i slope<prei p r e i pre_i prei 单增,和玩具装箱是同一类,维护单调递增斜率,最优决策扔队首就好。

因为还有一个切割次数的维度,所以可以对每一次切割 t ∈ [ 1 , k ] t\in[1,k] t[1,k],每次 t + 1 ← t t+1\leftarrow t t+1t 转移的时候做一次斜率优化 dp 即可。

题目还有一个要求就是求切割的方案,那也很好做,就是记录前缀数组 f a t i , t fat_{i,t} fati,t 表示枚举到 i i i,切割了 t t t 次的第 t t t 次切割点。由于单调队列直接维护反馈的就是最优决策点,那么用 f a t fat fat 数组记录每次的队首就好。

有一点要注意,因为 f a t fat fat 数组与单调队列维护的最优决策点挂钩,那么单调队列应当严谨维护。在上一篇题解,弹出队尾时时要写 ≤ \le 之外,还要把队首往后压,防止出现 0 0 0 的情况(其实就是把推式子的时候,把两个决策点 j 1 j1 j1 j 2 j2 j2 之间的符号改成 ≤ \le ,对答案 1 1 1 正确性没有影响,但是缩减了很多不必要的 0 0 0 决策点)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dd double
const ll N=1e5+9,K=202,inf=0x7f7f7f7f;
ll n,k,a[N];
ll f[N][K],q[N],pre[N];
int fat[N][K];
ll _2(ll x)
{return x*x;
}
dd slope(ll j1,ll j2,ll t)
{if(pre[j1]==pre[j2])return -inf;return (dd)(f[j1][t-1]-f[j2][t-1]+_2(pre[j2])-_2(pre[j1]))/(dd)(pre[j2]-pre[j1]);
}
int main()
{scanf("%lld%lld",&n,&k);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);pre[i]=pre[i-1]+a[i];}for(int t=1;t<=k;t++){ll hh=0,tt=0;for(int i=1;i<=n;i++){while(hh<tt&&slope(q[hh],q[hh+1],t)<=pre[i])hh++;f[i][t]=f[q[hh]][t-1]+pre[q[hh]]*(pre[i]-pre[q[hh]]);fat[i][t]=q[hh];while(hh<tt&&slope(q[tt],i,t)<=slope(q[tt-1],q[tt],t))tt--;q[++tt]=i;}}printf("%lld\n",f[n][k]);for(int tem=n,i=k;i>=1;i--){tem=fat[tem][i];printf("%d ",tem);}return 0;
}

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

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

相关文章

策略模式的C++实现示例

核心思想 策略模式是一种行为型设计模式&#xff0c;它定义了一系列算法&#xff0c;并将每个算法封装在独立的类中&#xff0c;使得它们可以互相替换。策略模式让算法的变化独立于使用它的客户端&#xff0c;从而使得客户端可以根据需要动态切换算法&#xff0c;而不需要修改…

Loki+Promtail+Grafana监控K8s日志

在现代云原生架构中&#xff0c;监控与日志管理对于确保系统稳定性和可靠性至关重要。Kubernetes&#xff08;K8s&#xff09;作为当下流行的容器编排平台&#xff0c;对日志的监控管理需求尤为突出。Loki, Promtail 和 Grafana 构成了一套强大的日志监控解决方案&#xff0c;它…

Git 批量合并 Commit 并且保留之前的 Commit 快速实现的思路

文章目录 需求Rebase / Pick / squashVim 的快速全局字符串替换 需求 我想把如下的提交 commit&#xff0c;变成一个 Commit&#xff0c;并且合并这些 Commit 的消息到一个节点 Rebase / Pick / squash 我合并到 5e59217 这个hash 上&#xff0c;这样合并后会保留两个 Commit…

基于海思soc的智能产品开发(芯片sdk和linux开发关系)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 随着国产化芯片的推进&#xff0c;在soc领域&#xff0c;越来越多的项目使用国产soc芯片。这些soc芯片&#xff0c;通常来说运行的os不是linux&…

将数据库结构化数据整合到RAG问答中的方式

**将数据库&#xff08;结构化数据&#xff09;接入 RAG&#xff08;Retrieval-Augmented Generation&#xff09;**的常见方式&#xff0c;并分别说明其实现方法、优点与缺点。 方式一&#xff1a;LLM 自动生成查询语句&#xff08;SQL/NoSQL&#xff09;直接访问数据库 方法…

论坛系统测试报告

目录 一、项目背景二、论坛系统测试用例思维导图三、论坛系统测试3.1界面测试3.2登陆测试3.3主页测试3.4个人中心测试 四、自动化测试脚本4.1配置驱动4.2创建浏览器类4.3功能测试4.3.1登陆测试4.3.2注册测试4.3.3主页测试4.3.4帖子编辑4.3.5运行主代码 五、BUG分析六、测试总结…

python量化交易——金融数据管理最佳实践——使用qteasy大批量自动拉取金融数据

文章目录 使用数据获取渠道自动填充数据QTEASY数据拉取功能数据拉取接口refill_data_source()数据拉取API的功能特性多渠道拉取数据实现下载流量控制实现错误重试日志记录其他功能 qteasy是一个功能全面且易用的量化交易策略框架&#xff0c; Github地址在这里。使用它&#x…

后端架构模式之-BFF(Backend-For-Frontend)

Backend-for-Frontend&#xff08;BFF&#xff09; 的概念与意义 1. 什么是 Backend-for-Frontend&#xff08;BFF&#xff09;&#xff1f; Backend-for-Frontend&#xff08;简称 BFF&#xff09;是一种后端架构模式&#xff0c;它为特定的前端应用&#xff08;Web、移动端…

upload-labs靶场 1-21通关

目录 1.Pass-01 前端绕过 分析 解题 2.Pass-02 服务器端检测--修改IMME 分析 解题 3.Pass-03 黑名单绕过 分析 解题 4.Pass-04 .htaccess绕过 分析 解题 5.Pass-05 . .绕过和.user.ini绕过 分析 解题 6.Pass-06 大小写绕过 分析 解题 7.Pass-07 空格绕过 分…

信贷风控系统架构设计

设计一个信贷风控系统需要综合考虑业务需求、技术架构、数据治理、合规安全等多个维度。以下是从顶级Java架构师视角的系统设计方案&#xff0c;分模块详细说明&#xff1a; 一、系统架构设计原则 高可用性&#xff1a;7x24小时服务&#xff0c;多机房容灾。低延迟&#xff1a…

Ubuntu20.04 在离线机器上安装 NVIDIA Container Toolkit

步骤 1.下载4个安装包 Index of /nvidia-docker/libnvidia-container/stable/ nvidia-container-toolkit-base_1.13.5-1_amd64.deb libnvidia-container1_1.13.5-1_amd64.deb libnvidia-container-tools_1.13.5-1_amd64.deb nvidia-container-toolkit_1.13.5-1_amd64.deb 步…

【工具】COME对比映射学习用于scRNA-seq数据的空间重构

介绍 单细胞RNA测序&#xff08;scRNA-seq&#xff09;能够在单细胞分辨率下实现高通量转录组分析。固有的空间位置对于理解单细胞如何协调多细胞功能和驱动疾病至关重要。然而&#xff0c;在组织分离过程中&#xff0c;空间信息常常丢失。空间转录组学&#xff08;ST&#xf…

Idea配置注释模板

一、配置类注释模板 打开IDEA&#xff0c;打开settings(快捷键&#xff1a;Ctrl Alt s)&#xff0c;选择Editor&#xff0c;找到File and Code Templates 这里以设置class文件为例&#xff0c;点击Class&#xff0c;在右侧配置以下内容 #if (${PACKAGE_NAME} && $…

pytorch高可用的设计策略和集成放大各自功能

在使用 PyTorch 编写模型时,为确保模型具备高可用性,可从模型设计、代码质量、训练过程、部署等多个方面采取相应的方法,以下为你详细介绍: 模型设计层面 模块化设计 实现方式:将模型拆分成多个小的、独立的模块,每个模块负责特定的功能。例如,在一个图像分类模型中,可…

从开源大模型工具Ollama存在安全隐患思考企业级大模型应用如何严守安全红线

近日&#xff0c;国家网络安全通报中心通报大模型工具Ollama默认配置存在未授权访问与模型窃取等安全隐患&#xff0c;引发了广泛关注。Ollama作为一款开源的大模型管理工具&#xff0c;在为用户提供便捷的同时&#xff0c;却因缺乏有效的安全管控机制&#xff0c;存在数据泄露…

初识Qt · 信号与槽 · 基础知识

目录 前言&#xff1a; 信号和槽初识 两个问题 前言&#xff1a; 本文我们正式开始介绍信号与槽这个概念&#xff0c;在谈及Qt中的信号与槽这个概念之前&#xff0c;我们不妨回顾一下Linux中的信号&#xff0c;比如发生了除0错误&#xff0c;OS就会给该进程发送一个信号&am…

Kotlin 5种单例模式

在Kotlin中实现单例模式有多种方法&#xff0c;以下是几种常见的方法&#xff1a; 饿汉式 饿汉式是最简单的一种实现方式&#xff0c;在类加载时就完成了实例的初始化。 //饿汉式 object Singleton1 {fun printMessage() {println("饿汉式")} }懒汉式 懒汉式是延迟…

探秘基带算法:从原理到5G时代的通信变革【一】引言

文章目录 一、引言1.1 研究背景与意义1.2 研究目的与方法1.3 研究内容与创新点 本博客为系列博客&#xff0c;主要讲解各基带算法的原理与应用&#xff0c;包括&#xff1a;viterbi解码、Turbo编解码、Polar编解码、CORDIC算法、CRC校验、FFT/DFT、QAMtiaozhi/解调、QPSK调制/解…

C/C++输入输出(1)

1.getchar和putchar 1.1getchar() 函数原型&#xff1a; 1 int getchar(void); getchar()函数返回用户从键盘输入的字符&#xff0c;使用时不带有任何参数。 程序运行到这个命令就会暂停&#xff0c;等待用户从键盘输入&#xff0c;等同于使用cin或scanf()方法读取一个字符…

【消息队列】数据库的数据管理

1. 数据库的选择 对于当前实现消息队列这样的一个中间件来说&#xff0c;具体要使用哪个数据库&#xff0c;是需要稍作考虑的&#xff0c;如果直接使用 MySQL 数据库也是能实现正常的功能&#xff0c;但是 MySQL 也是一个客户端服务器程序&#xff0c;也就意味着如果想在其他服…