CSP2019 Day2T2 划分
很显然有一个暴力 \(dp\),令 \(dp_{i, j}\) 表示最后一次划分在 \(i\) 上次划分在 \(j\) 的最小花费,令 \(S_i = \sum\limits_{j = 1} ^ i a_j\)。那么有转移:
\]
可以发现 \(dp_{j, k}\) 的值是和 \(i\) 无关的,只是 \(k\) 的取值范围和 \(i\) 有关,那么我们只需要知道 \(k\) 的取值范围再取这个范围内的最小值即可。将后面那个条件移项可得 \(S_k \ge 2 \times S_j - S_i\),因此 \(k\) 的取值范围应该在 \(2 \times S_j - S_i \sim j - 1\),而当 \(j\) 固定时,因为 \(S_i\) 随着 \(i\) 的增大单调递增,所以 \(k\) 能取到的下界不断减小,于是我们可以考虑在 \(i\) 的同时维护每个 \(j\) 的下界以及当前能取到的最小 \(dp_{j, k}\) 这样均摊复杂度就是 \(O(n ^ 2)\) 的了。
rep(i, 1, n) dp[i][0] = sum[i] * sum[i], p[i] = i - 1, mx[i] = inf;
rep(i, 2, n){
rep(j, 1, i - 1){
while(p[j] >= 0 && sum[p[j]] >= 2 * sum[j] - sum[i]) mx[j] = min(mx[j], dp[j][p[j]]), --p[j];
if(p[j] < j - 1) ++p[j];
if(mx[j] != inf) dp[i][j] = mx[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]);
}
}
rep(i, 0, n - 1) ans = min(ans, dp[n][i]);
printf("%lld", ans);
return 0;
如果这个 \(dp\) 不能压缩状态是显然无法优化的,但为了保证题目中的条件,我们根本无法压缩状态,下面一个常见的手段就是可以打个决策转移的表。可以发现我们每次转移 \(j \rightarrow i\) 都是选择了一个最大的能满足条件的 \(k\) 从 \(dp_{j, k} \rightarrow dp_{i, j}\),并且可以发现当 \(j\) 越大时 \(dp_{i, j}\) 越大。这样换句话来说就是每次我们让最后一段尽可能选择小的是更优的,实际上因为我们有 \((a + b) ^ 2 > a ^ 2 + b ^ 2\) 因此我们要尽可能多分段,如果我们每次选择最小的一段,不仅能够分更多的段,并且还能让后面尽可能分的段更多,并且能让当前的贡献更小,所以这样做一定是更优的。针对这个贪心,我们可以令 \(dp_i\) 表示当前划分到以 \(i\) 结尾的段的最小花费,令 \(p_i\) 表示 \(i\) 是由哪里转移来的,那么每次转移就要找到一个最大 \(j\) 满足 \(S_i - S_j \ge S_j - S_{p_j}\) 移项可得 \(S_i \ge 2 \times S_j - S_{p_j}\),我们大可以使用线段树二分出左边第一个比某个数大的位置,但实际上这里还有个 \(O(n)\) 做法。可以发现因为 \(S_i\) 是单调递增的,因此我们之前能够选择的 \(j\) 随着 \(i\) 的增大一定是还能继续选的。因此我们可以维护一个类似单调队列的东西,在队列中 \(2 \times S_j - S_{p_j}\) 单调递增,每次我新加入一个元素,从队尾一直开始弹出知道这个 \(2 \times S_{q_t} - S_{p_{q_t}} < 2 \times S_j - S_{p_j}\) 为止,那么这样我们获得的每个值的位置都将是当前最靠右的点,并且由于 \(S_i\) 在不断单调递增,我们只需要每次不断移动队尾找到队列中最后一个 \(\le S_i\) 的元素即可。
dp[1] = a[1] * a[1], h = 1, t = 2, q[1] = 0, q[2] = 1;
rep(i, 2, n){
while(h <= t && 2 * sum[q[h]] - sum[p[q[h]]] <= sum[i]) ++h;
--h, p[i] = q[h];
dp[i] = dp[p[i]] + (sum[i] - sum[p[i]]) * (sum[i] - sum[p[i]]);
while(h <= t && 2 * sum[i] - sum[p[i]] <= 2 * sum[q[t]] - sum[p[q[t]]]) --t;
q[++t] = i;
}
printf("%lld", dp[n]);
最后那 \(12\) 分需要使用高精度,根据我们的流程可以看出实际上我们不需要记录 \(dp\) 数组,只需要记录之前的 \(p\) 即可,最后我们只需要不断往下迭代加上每一段的值即可,我的高精太丑了(其实是写的 \(\_\_int128\))就不贴了。
CSP2019 Day2T2 划分的更多相关文章
- CSP2019 D2T2 划分 (单调队列DP)
题目 洛谷传送门 题解 就是这道题搞我退役考场上写了n^2 64分,结果爆成8-12分.直接GG. 考场上想到正解的写法被自己否决了 题解传送门(看到这道送我退役的题目⑧太想写题解) 六行O(n2)O ...
- 【CSP2019】题解合集
诈个尸 先挖坑 虽然连去都没去但还是想做做 今年貌似比去年还毒瘤啊... yrx.hjw都进了省队线tql orz (myh:没AK真丢脸 Day1T1 格雷码 Day1T2 括号树 Day1T3 树 ...
- CSP-S 2019 Solution
Day1-T1 格雷码(code) 格雷码是一种特殊的 \(n\) 位二进制串排列法,要求相邻的两个二进制串恰好有一位不同,环状相邻. 生成方法: \(1\) 位格雷码由两个 \(1\) 位的二进制串 ...
- CSP2019 S2滚粗记
最好分数:100+20+10+64+64+55 最坏分数:100+20+10+64+36+55 咕咕数据分数:100+25+10+64+60+55 CCF官方: 100+35+10+64+36+55= ...
- CSP2019题解
CSP2019题解 格雷码 按照生成的规则模拟一下即可. 代码 括号树 看到括号匹配首先想到用栈,然后又在树上就可以想到可追溯化栈. 令\(a_i=1\)表示\(i\)号节点上的括号为(,否则为), ...
- CSP2019 题解
CSP2019 题解 D1T1 格雷码(code) 题目传送门 https://loj.ac/problem/3208 题解 按照题意模拟就可以了. 对于第 \(i\) 位,如果 \(k \geq 2 ...
- [LeetCode] Partition List 划分链表
Given a linked list and a value x, partition it such that all nodes less than x come before nodes gr ...
- SWMM模型子汇水区划分的几种方法
子汇水区的划分是SWMM模型建模的主要步骤之一,划分的好坏对结果精度有比较大的影响.概括来讲,子汇水区的划分有以下几种思路: (1)根据管网走向.建筑物和街道分布,直接人工划分子汇水区.这个方法适用于 ...
- 等价类划分方法的应用(jsp)
[问题描述] 在三个文本框中输入字符串,要求均为1到6个英文字符或数字,按submit提交. [划分等价类] 条件1: 字符合法; 条件2: 输入1长度合法; 条件3: 输入2长度合法: 条件4: 输 ...
随机推荐
- SQL 行转列===列转行
行转列:sum+if 在长表的数据组织结构中,同一uid对应了多行,即每门课程一条记录,对应一组分数,而在宽表中需要将其变成同一uid下仅对应一行 在长表中,仅有一列记录了课程成绩,但在宽表中则每门课 ...
- [linux]ubuntu18.04 屏幕分辨率不适应问题
今天换了新显示器,发现更大的屏幕不适应原有的屏幕分辨率,看起来特别变扭. 在设置处查看最高分辨率仅为1024*748,没有与屏幕相适应的1920*1080(16:9). 解决方式: 1. 终端输入命令 ...
- Python学习笔记:利用pd.get_dummies实现哑变量编码
一.理论介绍 虚拟变量(dummy variable)也叫哑变量,是一种将多分类变量转换为二分变量的一种形式. 如果多分类变量有k个类别,则可以转化为k-1个二分变量. 需要有一个参照的类别. 在非线 ...
- CF149D游戏
题目描述 Petya遇到了一个关于括号序列的问题: 给定一个字符串S,它代表着正确的括号序列,即("(")与 (")")是匹配的.例如:"(())() ...
- python 之 pip、pypdf2 安装与卸载
pip是个啥? pip 是一个现代的,通用的 Python 包管理工具.提供了对 Python 包的查找.下载.安装.卸载的功能. 第一步:pip 下载:https://pypi.org/projec ...
- day2 数组字符串逆序存放正序对接调试
这个问题仔细想了想,是s,t,s[],t[],重定义了,导致输入的是s,t这个定义变量,与传参传的是指针变量就不匹配了. 如果加上对s,t的地址,让传参的形式想匹配,还是报错,这块也没有弄懂,初步觉的 ...
- 《剑指offer》面试题21. 调整数组顺序使奇数位于偶数前面
问题描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 示例: 输入:nums = [1,2,3,4] 输出:[1,3,2,4] ...
- Discuz!X V3.4后台任意文件删除
Discuz!X V3.4后台任意文件删除 简述 该漏洞为后台任意文件删除,需要有管理员的权限,所以说危害非常小 复现环境 docker.vulhub-master 项目地址:https://gite ...
- 解决windows下因为防火墙无法通过go get 下载gin的问题
使用: go get -u github.com/gin-gonic/gin 出现以下错误: unrecognized import path "gopkg.in/yaml.v2" ...
- php中关于数据库的操作
php中对数据库的操作,举例如下: //连接到本地mysql数据库 $myconn=mysql_connect("localhost","root"," ...