## 非常神仙的 wqs 二分优化dp,又学了一招。

首先我们需要先想到一个人类智慧版的前缀和优化。

# part 1:violence

然鹅在前缀和优化之前我们先考虑暴力做法:
我们可以枚举 i 、 j 令其表示前 i 个村庄设立 j 个邮局的最小贡献。
然后枚举 k 表示前 k 个村庄已经设立邮局,现在处理 k+1~i 的村庄。
接着再枚举当前邮局设立在哪里,然后 O(n) 累加每个村庄的贡献。
这样的复杂度是 O(n^5) 的,也许达不到这个上限,但是 O(n^4) 的时间总是要的。
于是这样...已经炸掉了。

# part 2:optimization(human wisdom)

我们考虑在一段区间内建立一个邮局,那么这个邮局会使得附近村庄的贡献降低。
那么如何使得这个降低的贡献最大呢?我们可以由 **~~人类智慧~~ 推论** 得出:
当我们将邮局设立在一个要产生贡献的区间的中点时,降低的贡献最大。
那么这时我们不妨设区间中点坐标为 k ,左端点坐标 i ,右端点坐标 j 。
此时这段区间对答案的贡献为:# $$(S[j]-S[k])-a[k] \times (j-k) + a[k] \times (k-i)-(S[k]-S[i])$$ #

那么这样的复杂度是 O(n^3) 的,已经有了较大进步,起码30分是到手了。

# part 3:optimization(Quadrilateral inequality)

于是我们考虑进一步优化,看到  满数据 是 3e3 的数据范围,那么应该是要用 O(n^2) 的算法。
那这里就要用 四边形不等式优化了(我不会)。同学们可以自行研究,大概就是根据 f[i][j] 的一个性质:
f[i][j]+f[i-1][j+1]>f[i-1][j]+f[i][j+1] => f[i][j] 的决策点在 f[i-1][j] 和 f[i][j+1] 之间之类的。
(怎么证我就母鸡了)

于是 O(n^2) 满分。

# part4:optimization(wqs binary cut)

然鹅我们还可以考虑进一步升华算法。

我们可以考虑二分将算法复杂度优化成 O(nlogn)

而且是 wqs 二分。

如何二分? 我们考虑给区间的每次分割增加一个贡献。
那么我们可以看出:增加贡献越大,将会分割的次数就越少。
容易想到,当分割出的段数恰好为 m 时,该状态下的 f[n] 减去增加贡献就是答案。
这样一个 log 去了。 那么怎么 O(n) 转移方程?
我们用单调队列优化转移,单调队列内每个点记录上次转移位置以及其控制的后方最优解范围。

也就是说,最后一种算法是用了二分优化 part3 ,将一个 n 变成了 log 。

# part 5:coding(s)

$$ O(n^3) $$

 //by Judge
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
const int M=;
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
int x=,f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
} int n,m,a[M],s[M],f[M][M];
inline void cmin(int& a,int b){ if(a>b) a=b; }
int main(){
n=read(),m=read(),memset(f,0x3f,sizeof(f)),f[][]=;
for(int i=;i<=n;++i) a[i]=read(); std::sort(a+,a++n);
for(int i=;i<=n;++i) s[i]=s[i-]+a[i];
for(int i=;i<=n;++i) for(int j=;j<=m;++j) for(int k=,t;k<i;++k)
t=i+k+>>,cmin(f[i][j],f[k][j-]+(s[i]-s[t])-a[t]*(i-t)+a[t]*(t-k)-(s[t]-s[k]));
return printf("%d\n",f[n][m]),;
}

n^3

$$ O(n^2) $$

 //by Judge
#include<algorithm>
#include<iostream>
#include<cstdio>
#define ll long long
const int M=;
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
int x=,f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
} int n,m,a[M],mk[M][M]; ll f[M][M],w[M][M];
int main(){
n=read(),m=read();
for(int i=;i<=n;++i) a[i]=read(); std::sort(a+,a++n);
for(int i=;i<=n;++i) for(int j=i+;j<=n;++j)
w[i][j]=w[i][j-]+a[j]-a[i+j>>];
for(int i=;i<=n;++i) f[][i]=w[][i],mk[][i]=;
for(int i=;i<=m;++i){ mk[i][n+]=n;
for(int j=n;j>i;--j){
f[i][j]=1ll<<;
for(int k=mk[i-][j];k<=mk[i][j+];++k)
if(f[i][j]>f[i-][k]+w[k+][j])
f[i][j]=f[i-][k]+w[k+][j],mk[i][j]=k;
}
} return printf("%lld\n",f[m][n]),;
}

n^2

$$ O(n logn) $$

 //by Judge
#include<algorithm>
#include<iostream>
#include<cstdio>
#define mid (l+r>>1)
#define ll long long
using namespace std;
const int M=1e5+;
const ll inf=1e18+;
#ifndef Judge
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
int x=,f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
} int n,m,las[M]; ll a[M],S[M],f[M];
struct node{ int pos,l,r; // l~r 为 pos 点所控制的最优区间
node(int pos,int l,int r):pos(pos),l(l),r(r){} node(){}
}Q[M];
inline ll calc(int l,int r,int tim){
if(l>=r) return inf; int t=l+r+>>; //人类智慧+前缀和优化
return f[l]+(S[r]-S[t])-(r-t)*a[t]+(t-l)*a[t]-(S[t]-S[l])+tim;
} inline bool check(int tim){
int siz=,ans=; Q[]=node(,,n);
for(int i=;i<=n;++i){ int l=,r=siz,pos;
while(l<=r) Q[mid].l<=i?l=(pos=mid)+:r=mid-;
f[i]=calc(Q[pos].pos,i,tim),las[i]=Q[pos].pos,pos=n+;
while(siz&&calc(Q[siz].pos,Q[siz].l,tim)>=calc(i,Q[siz].l,tim)) pos=Q[siz--].l;
if(siz && calc(Q[siz].pos,Q[siz].r,tim)>=calc(i,Q[siz].r,tim)){ l=Q[siz].l,r=Q[siz].r;
while(l<=r) calc(Q[siz].pos,mid,tim)>=calc(i,mid,tim)?r=(pos=mid)-:l=mid+;
Q[siz].r=pos-;
} if(pos!=n+) Q[++siz]=node(i,pos,n);
} for(int i=n;i;i=las[i]) ++ans; return ans<m;
}
int main(){
n=read(),m=read();
for(int i=;i<=n;++i) a[i]=read();
sort(a+,a++n);
for(int i=;i<=n;++i) S[i]=S[i-]+a[i];
int l=,r=5e6; ll ans=;
while(l<=r) check(mid)?r=mid-:(ans=f[n]-m*mid,l=mid+);
return printf("%lld\n",ans),;
}

n log n

[IOI2000] 邮局的更多相关文章

  1. COGS 1507. [IOI2000]邮局

    1507. [IOI2000]邮局 ★☆   输入文件:postoffice.in   输出文件:postoffice.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] ...

  2. BZOJXXXX: [IOI2000]邮局——四边形不等式优化初探

    貌似$BZOJ$上并没有这个题... 是嫌这个题水了么... 还是要氪金权限号??? 这里附上洛谷的题面:洛谷P4767 [IOI2000]邮局 题目描述 高速公路旁边有一些村庄.高速公路表示为整数轴 ...

  3. 题解——洛谷P4767 [IOI2000]邮局(区间DP)

    这题是一道区间DP 思维难度主要集中在如何预处理距离上 由生活经验得,邮局放在中间显然最优 所以我们可以递推求出\( w[i][j] \)表示i,j之间放一个邮局得距离 然后设出状态转移方程 设\( ...

  4. [LUOGU] P4767 [IOI2000]邮局

    https://www.luogu.org/problemnew/show/P4767 四边形不等式好题! 可以设f[i][j]表示前i个村庄,建了j个邮局的最小代价. 转移:f[i][j]=min{ ...

  5. luogu_P4767 [IOI2000]邮局

    传送门 Description 高速公路旁边有一些村庄.高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识.没有两个在同样地方的村庄.两个位置之间的距离是其整数坐标差的绝对值. 邮局将建在一些,但 ...

  6. P4767 [IOI2000]邮局 - 平行四边形不等式优化DP

    There is a straight highway with villages alongside the highway. The highway is represented as an in ...

  7. NOIP2018备考——DP专题练习

    P4095 [HEOI2013]Eden 的新背包问题   P2657 [SCOI2009]windy数   P3413 SAC#1 - 萌数   P3205 [HNOI2010]合唱队   P476 ...

  8. DP的优化

    参考资料: 李煜东<算法竞赛进阶指南> 斜率优化 形如: \(f[i] = min\{f[j]+val(i,j)\}\)的dp,多项式\(val(i,j)\)包含\(i,j\)的乘积项 引 ...

  9. 我的刷题单(8/37)(dalao珂来享受切题的快感

    P2324 [SCOI2005]骑士精神 CF724B Batch Sort CF460C Present CF482A Diverse Permutation CF425A Sereja and S ...

随机推荐

  1. Hbase记录-hbase部署

    #版本支持 #官网下载二进制包,解压到/usr/app下,配置/etc/profile: export HBASE_HOME=/usr/app/hbase export PATH=$HBASE_HOM ...

  2. 转---变量LEGB规则

    Python 变量作用域的规则是 LEGB LEGB含义解释: L -- Local(function):函数内的名字空间 E -- Enclosing function locals:外部嵌套函数的 ...

  3. 043、data-packed volume container (2019-03-06 周三)

    参考https://www.cnblogs.com/CloudMan6/p/7203285.html     volume container 的数据归根到底还是在host上,我们能不能把数据完全放到 ...

  4. Map接口、HashMap类、LinkedHashSet类

    java.util.Map<K, V>接口 双列集合,key不可以重复 Map方法: 1.public V put(K key, V value):键值对添加到map,如果key不重复返回 ...

  5. 开源ETL工具之Kettle介绍

    What 起源 Kettle是一个Java编写的ETL工具,主作者是Matt Casters,2003年就开始了这个项目,最新稳定版为7.1. 2005年12月,Kettle从2.1版本开始进入了开源 ...

  6. C# WinForm 多线程 应用程序退出的方法 结束子线程

    1.this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit(); 强制所有消息中止,退 ...

  7. GCC编译器原理(二)------编译原理一:ELF文件(1)

    二.ELF 文件介绍 2.1 可执行文件格式综述 相对于其它文件类型,可执行文件可能是一个操作系统中最重要的文件类型,因为它们是完成操作的真正执行者.可执行文件的大小.运行速度.资源占用情况以及可扩展 ...

  8. 十二、文件和目录——Linux文件系统结构

    12.1 Linux文件系统结构 12.1.1 文件操作基本元素 文件操作相关的最基本元素是:目录结构,索引节点和文件的数据本身 目录结构(目录项) 索引节点(i 节点) 文件的数据 12.1.2 文 ...

  9. UIScrollView嵌套的完美解决方案

    UIScrollView嵌套的完美解决方案 做iOS开发,不可避免的会遇到UIScrollView的嵌套问题,之前也曾遇到过,吭哧吭哧做完了,效果不理想,和产品大战好几回合,就那样了.不可避免的,又一 ...

  10. cpp #,##

    #define语句中的#是把参数字符串化,##是连接两个参数成为一个整体. #define FACTORY_REF(name) { #name, Make##name } 中#name就是将传入的na ...