[CSP2019] 划分 解题报告
题意
有 \(n\) 个非负整数 \(a_i\) \(( n \le 4 * 10^7)\), 将它们分为若干部分, 记为 \(S_i\), 要求 \(S_{i+1} \ge S_i\),
设 \(res=\sum_{i=1}^{k} S_i^2\).
求 \(res\) 的最小值
思路
64 pts
考场上写的做法.
设 \(f[i][j]\) 为第一段的起点为 \(i\), 终点为 \(j\) 时 \(res\) 的最小值.
朴素做法直接枚举 \(i,j,k\), 将 \(f[i][j]\) 从 \(f[j][k]\) 转移过来.
优化:
考虑中间的转移点 \(j\), 对于每个 \(i<j\), 合法的 \(k\) 的范围是递增的, 所以可以只枚举 \(i,j\) 然后指针 \(k\) 根据 \(sum_j-sum_{i-1}\) 不断忘前移, 并取最小值更新即可.
88 pts
设总共有 \(k\) 个块, 可以得出两个性质.
性质 1 : \(k\) 越大, 方案越优.
感性理解 : \(a^2+b^2 \le (a+b)^2\).
进一步推断出,
性质 2 : \(S_k\) 越小, 方案越优.
感性理解 : \(S_k\) 越小, 为了使得方案合法, 就强迫前面的 \(S\) 也要尽量地小, 由于 \(a\) 是非负整数, 所以 \(S\) 的值越小, \(k\) 就越大, 根据性质 1, 方案就越优.
这样的话我们就可以维护一个单调队列, 对于每一位, 找到以它为结束点时, 最小的 \(S_k\).
在把新元素加入单调队列时还有一些细节, 详见代码.
100pts
上面的做法加个高精 (高精这种东西早就忘光了好吗...)
代码
64pts
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e3+7;
const ll inf=5e18;
int n,ty;
ll a[N],sum[N],f[N][N],ans=inf;
void read(){ }
ll p2(ll x){ return x*x; }
int main(){
//freopen("divd.in","r",stdin);
cin>>n>>ty;
if(ty) read();
else for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; }
memset(f,127,sizeof(f));
for(int i=1;i<=n;i++) f[i][n]=p2(sum[n]-sum[i-1]);
for(int j=n;j>=1;j--){
int k=n;
ll minx=inf;
for(int i=1;i<=j;i++){
while(p2(sum[j]-sum[i-1])<=p2(sum[k]-sum[j])){
minx=min(minx,f[j+1][k]);
k--;
}
f[i][j]=min(f[i][j],minx+p2(sum[j]-sum[i-1]));
}
}
for(int i=1;i<=n;i++) ans=min(ans,f[1][i]);
printf("%lld\n",ans);
return 0;
}
88pts
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e7+7;
int n,ty,pre[N],que[N],t1,t2;
ll a[N],sum[N],lst[N],ans;
void read(){};
ll p2(ll x){ return x*x; }
int main(){
// freopen("divd.in","r",stdin);
// freopen("x.out","w",stdout);
cin>>n>>ty;
if(ty) read();
else for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; }
t1=t2=1;
que[1]=0;
for(int i=1;i<=n;i++){
// printf("%d: ",i); for(int j=t1;j<=t2;j++) printf("%d ",que[j]); putchar('\n');
while(t1<t2&&lst[que[t1+1]]<=sum[i]-sum[que[t1+1]]) t1++;
pre[i]=que[t1];
lst[i]=sum[i]-sum[pre[i]];
while(t2>=t1&&lst[que[t2]]>=lst[i]+sum[i]-sum[que[t2]]) t2--;
que[++t2]=i;
}
int p=n;
while(p){
ans+=lst[p]*lst[p];
p=pre[p];
}
// for(int i=1;i<=n;i++) printf("pre[%d]: %d\n",i,pre[i]);
printf("%lld\n",ans);
return 0;
}
原文地址:https://www.cnblogs.com/brucew-07/p/11896792.html
- 【Spring实战】—— 15 Spring JDBC模板使用
- 前端开发总览
- 【Spring实战】—— 16 基于JDBC持久化的事务管理
- 【Spring实战】—— 4 Spring中bean的init和destroy方法讲解
- 基于AngularJS的过滤与排序
- 【Spring实战】—— 5 设值注入
- 科学家预测:未来100万年人类将变成半机械人类
- 【Spring实战】—— 8 自动装配
- 【Spring实战】—— 7 复杂集合类型的注入
- 【Spring实战】—— 6 内部Bean
- 几款可替代Dreamweaver的HTML5开发工具
- Linux下的Telnet设置方法介绍
- 2017年11月互联网和相关服务业保持快速增长
- 深度学习胸部x射线
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP信号处理机制的操作代码讲解
- php防止表单重复提交实例讲解
- Python实现封装打包自己写的代码,被python import
- 创建一个 Serverless 应用,真的没有这么难!
- PHP使用mongoclient简单操作mongodb数据库示例
- 基于TensorFlow的CNN实现Mnist手写数字识别
- django rest framework 自定义返回方式
- PHP+Ajax实现的检测用户名功能简单示例
- Yii框架学习笔记之session与cookie简单操作示例
- Ajax+Jpgraph实现的动态折线图功能示例
- Python闭包及装饰器运行原理解析
- Django中Q查询及Q()对象 F查询及F()对象用法
- keras.layer.input()用法说明
- python入门:argparse浅析 nargs='+'作用
- PHP7导出Excel报ERR_EMPTY_RESPONSE解决方法