征途

Pine开始了从S地到T地的征途。

从S地到T地的路可以划分成\(n\)段,相邻两段路的分界点设有休息站。

Pine计划用\(m\)天到达T地。除第\(m\)天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。

Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。

帮助Pine求出最小方差是多少。

设方差是\(v\),可以证明,\(v\times m^2\)是一个整数。为了避免精度误差,输出结果时输出\(v\times m^2\)。

对于 \(100\%\) 的数据,\(1 \le n \le 3000\)

题解

先推导一波方差。

\[s^2=\frac 1m\sum_{i=1}^m(\overline{v}-v_i)^2\\
=\frac 1m\left(m\overline{v}^2-2\overline{v}\sum_{i=1}^mv_i+\sum_{i=1}^m v_i^2\right)\\
=-\overline{v}^2+\frac 1m\sum_{i=1}^m v_i^2
\]

答案为

\[s^2m^2=-\left(\sum_{i=1}^mv_i^2\right)+m\sum_{i=1}^mv_i^2
\]

所以要最小化的就是\(\sum_{i=1}^mv_i^2\)。

斜率优化

设\(f(i,j)\)表示前\(i\)天走了前\(j\)段路程的最小代价。

\[f(i,j)=\min_{0\le k<j}\{f(i-1,k)+(s_j-s_k)^2\}
\]

化成一次函数式

\[2s_js_k+f(i,j)-s_j^2=f(i-1,k)+s_k^2
\]

点坐标很明显,单调队列维护下凸包即可。

时间复杂度\(O(mn)\)。话说我怎么几次都把\(j\)写成\(i\),调了好久。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T>il T read(T&x){
return x=read<T>();
}
using namespace std;
#define int long long co int N=3003;
int n,m,s[N];
int f[N][N],q[N]; int up(int i,int k1,int k2){
return f[i][k2]+s[k2]*s[k2]-f[i][k1]-s[k1]*s[k1];
}
int down(int k1,int k2){
return s[k2]-s[k1];
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;++i) s[i]=s[i-1]+read<int>();
for(int i=1;i<=n;++i) f[1][i]=s[i]*s[i];
for(int i=2;i<=m;++i){
int l=0,r=0;
for(int j=1;j<=n;++j){
while(l<r&&2LL*s[j]*down(q[l],q[l+1])>=up(i-1,q[l],q[l+1])) ++l;
int k=q[l];
f[i][j]=f[i-1][k]+(s[j]-s[k])*(s[j]-s[k]);
while(l<r&&up(i-1,q[r],j)*down(q[r-1],q[r])<=up(i-1,q[r-1],q[r])*down(q[r],j)) --r;
q[++r]=j; // edit 1:i->j
}
}
printf("%lld\n",m*f[m][n]-s[n]*s[n]);
return 0;
}

导数零点二分

这道题更像一个wqs二分的模板题,我现在把它叫做导数零点二分。

为什么呢?推荐FlashHu的博客。

考虑\(最优解-段数\)的函数图像,给每一段追加的权值相当于左右移动导函数的零点。我们要做的就是让零点恰好是我们要求的段数。

时间复杂度\(O(n \log s_n)\)。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T>il T read(T&x){
return x=read<T>();
}
using namespace std;
#define int long long co int N=3003;
int n,m,s[N];
int f[N],g[N];
int q[N],l,r; int up(int a,int b){
return f[b]+s[b]*s[b]-f[a]-s[a]*s[a];
}
int down(int a,int b){
return s[b]-s[a];
}
void solve(int w){
q[l=r=0]=0;
for(int i=1;i<=n;++i){
while(l<r&&2*s[i]*down(q[l],q[l+1])>up(q[l],q[l+1])) ++l; // edit 1:>=
f[i]=f[q[l]]+w+(s[i]-s[q[l]])*(s[i]-s[q[l]]),g[i]=g[q[l]]+1;
while(l<r&&up(q[r],i)*down(q[r-1],q[r])<up(q[r-1],q[r])*down(q[r],i)) --r;
q[++r]=i;
}
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;++i) s[i]=s[i-1]+read<int>();
int l=0,r=s[n]*s[n]/m,ans;
while(l<r){
int mid=(l+r)>>1;
solve(mid);
if(g[n]>m) l=mid+1;
else ans=m*(f[n]-mid*m)-s[n]*s[n],r=mid;
}
printf("%lld\n",ans);
return 0;
}

「SDOI2016」征途的更多相关文章

  1. 「SDOI2016」征途 题解

    「SDOI2016」征途 先浅浅复制一个方差 显然dp,可以搞一个 \(dp[i][j]\)为前i段路程j天到达的最小方差 开始暴力转移 \(dp[i][j]=min(dp[k][j-1]+?)(j- ...

  2. 【LOJ】#2035. 「SDOI2016」征途

    题解 有人管它叫带权二分,有人管它叫dp凸优化,有人管它叫wqs二分-- 延伸出来还有zgl分治,xjp¥!%#!@#¥!# 当我没说 我们拆个式子,很容易发现所求的就是 \(m\sum_{i = 1 ...

  3. loj2035 「SDOI2016」征途

    学了斜率优化这题就能一气呵成地做出来啦qwqqwq #include <iostream> #include <cstdio> using namespace std; typ ...

  4. 「SDOI2016」储能表(数位dp)

    「SDOI2016」储能表(数位dp) 神仙数位 \(dp\) 系列 可能我做题做得少 \(QAQ\) \(f[i][0/1][0/1][0/1]\) 表示第 \(i\) 位 \(n\) 是否到达上界 ...

  5. [LOJ 2070] 「SDOI2016」平凡的骰子

    [LOJ 2070] 「SDOI2016」平凡的骰子 [题目链接] 链接 [题解] 原题求的是球面面积 可以理解为首先求多面体重心,然后算球面多边形的面积 求重心需要将多面体进行四面体剖分,从而计算出 ...

  6. liberOJ #2033. 「SDOI2016」生成魔咒 后缀数组

    #2033. 「SDOI2016」生成魔咒     题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1 11.2 22 拼凑起来形成一个魔咒串 [1,2] [1, 2] ...

  7. 「SDOI2016」数字配对

    「SDOI2016」数字配对 题目大意 传送门 题解 \(a_i\) 是 \(a_j\) 的倍数,且 \(\frac{a_i}{a_j}\) 是一个质数,则将 \(a_i,a_j\) 质因数分解后,其 ...

  8. 【LOJ】#2070. 「SDOI2016」平凡的骰子

    题解 用了一堆迷之复杂的结论结果迷之好写的计算几何???? 好吧,要写立体几何了 如果有名词不懂自己搜吧 首先我们求重心,我们可以求带权重心,也就是x坐标的话是所有分割的小四面体的x坐标 * 四面体体 ...

  9. 【LOJ】#2069. 「SDOI2016」齿轮

    题解 我一开始还努力想这道题是不是有坑,被SDOI折磨到我觉得不能有那么水的题在-- 就是带权并查集维护一下两点间距离,如果新加一条边两个点在同一集合,看看已有的路径和新加的路径是否相等 乘积可以在模 ...

随机推荐

  1. LeetCode 503. 下一个更大元素 II(Next Greater Element II)

    503. 下一个更大元素 II 503. Next Greater Element II 题目描述 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素.数字 ...

  2. ByteBuffer常见方法

    ByteBuffer的三个属性 limit:所有对Buffer读写操作都会以limit变量的值作为上限. position:代表对缓冲区进行读写时,当前游标的位置. capacity:代表缓冲区的最大 ...

  3. 转发:for /f命令之—Delims和Tokens用法&总结

    在For命令语踞饽参数F中,最难理解的就是Delims和Tokens两个选项,本文简单的做一个比较和总拮.“For /f”常用来解析文本,读取字符串.分工上,delims负责切分字符串,而tokens ...

  4. 图片url地址的生成获取方法

    在写博客插入图片时,许多时候需要提供图片的url地址.作为菜鸡的我,自然是一脸懵逼.那么什么是所谓的url地址呢?又该如何获取图片的url地址呢? 首先来看一下度娘对url地址的解释:url是统一资源 ...

  5. Django模型层之更多操作

    Django模型层之更多操作 一 .ORM字段 1.1 常用字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为 ...

  6. git学习笔记 ---管理修改

    现在,假定你已经完全掌握了暂存区的概念.下面,我们要讨论的就是,为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件. 你会问,什么是修改?比如你新增了一行,这就是一个修改 ...

  7. 环境配置--升级Python 3.6爬坑

    升级到3.6之后,发现ctrl alt t呼不出命令台,找了半天发现update manager也打不开,而且没有错误报告.....查阅了一番资料看到有人有类似的问题(https://askubunt ...

  8. 单例模式的双重锁为什么要加volatile(转)

    单例模式如下: 需要volatile关键字的原因是,在并发情况下,如果没有volatile关键字,在第5行会出现问题. instance = new TestInstance();可以分解为3行伪代码 ...

  9. 织梦dedecms是什么?

    织梦dedecms是什么?DedeCms是织梦内容管理系统(DedeCms)的简称, 以简单.实用.开源而闻名,是国内最知名的PHP开源网站管理系统,也是使用用户最多的PHP类CMS系统; DedeC ...

  10. 前端js入门之 JavaScript 对象直接量

    JavaScript中,创建对象可以使用构造函数方式. 代码实例如下: obj.webName = "博客园"; obj.address = "编程语言"; 以 ...