[IOI2000] 邮局 加强版
CXXXVI.[IOI2000] 邮局 加强版
Observation 1. 若一段村庄中设一个邮局,则邮局一定设在其中位数(若是偶数则任一中位数)的位置。
Observation 2. 若令 为区间 中村庄设一个邮局的费用,则其满足四边形不等式。
Observation 3. 显然全段中修几个邮局的函数有凹性,可以wqs二分。
考虑check二分的 。则我们有转移式
当转移分层时,因为四边形不等式得出的决策单调性,可以分治解决;不分层时,一种解法是使用CDQ分治,正如CXXX.[GYM102904B]Dispatch Money。但是那题囿于逆序对没有 的在线计算方法,所以只能莫队式解决;而本题的 函数是可以简单 在线解决的,因此没有必要使用CDQ分治。
考虑我们当前已经求出了 所有的 值。此时,位置 转移的位置必定是从 开始的一段(当然可能为空),位置 转移的位置必定是紧接着 的转移段后的一段(仍可能为空)……位置 是紧接着 的转移段往后一直到结尾的一段。
显然,此时 最优转移点已经明确,是从左到右第一个非空的段。但是我们要让其它东西也可以从 转移来。
明显, 转移到的位置必定是一段后缀。于是我们从右往左枚举每一段,若这段最左方的位置从 转移更优,显然这一整段都应从 转移,然后再判断下一段;否则,就在这段中二分出 更优的后缀,然后这段后缀,再加上之前已经判断为 转移的那些段,就是 的转移段。
这样,内层DP的复杂度就是 的;再套上wqs二分,复杂度就是 的。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[500100],F[500100],G[500100],pos[500100],trs[500100],l,r;
ll s[500100],f[500100],g[500100];
ll calc(int l,int r){return s[l-1]+s[r]-s[(l+r)/2]-s[(l+r-1)/2];}
void read(int &x){
x=0;
char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
int che(ll ip){
// printf("%lld\n",ip);
l=r=1,trs[l]=0;
for(int i=1;i<=n;i++){
if(l<r&&pos[l]==i)l++;
f[i]=f[trs[l]]+calc(trs[l]+1,i)+ip,F[i]=F[trs[l]]+1;
if(i==n)break;
pos[l-1]=i+1,pos[r]=n+1;
while(true){
if(r<l){trs[++r]=i;break;}
if(f[i]+calc(i+1,pos[r-1])<=f[trs[r]]+calc(trs[r]+1,pos[r-1])){r--;continue;}
int L=pos[r-1],R=pos[r];
while(L+1<R){
int mid=(L+R)>>1;
if(f[i]+calc(i+1,mid)<=f[trs[r]]+calc(trs[r]+1,mid))R=mid;
else L=mid;
}
if(R<=n)r++,pos[r-1]=R,trs[r]=i;
break;
}
}
// for(int i=1;i<=n;i++)printf("%lld ",f[i]);puts("");
// for(int i=1;i<=n;i++)printf("%d ",F[i]);puts("");
l=r=1,trs[l]=0;
for(int i=1;i<=n;i++){
if(l<r&&pos[l]==i)l++;
g[i]=g[trs[l]]+calc(trs[l]+1,i)+ip,G[i]=G[trs[l]]+1;
if(i==n)break;
pos[l-1]=i+1,pos[r]=n+1;
while(true){
if(r<l){trs[++r]=i;break;}
if(g[i]+calc(i+1,pos[r-1])<g[trs[r]]+calc(trs[r]+1,pos[r-1])){r--;continue;}
int L=pos[r-1],R=pos[r];
while(L+1<R){
int mid=(L+R)>>1;
if(g[i]+calc(i+1,mid)<g[trs[r]]+calc(trs[r]+1,mid))R=mid;
else L=mid;
}
if(R<=n)r++,pos[r-1]=R,trs[r]=i;
break;
}
}
int R=F[n],L=G[n];
// printf("%d %d\n",L,R);
if(L>m)return 1;
if(R<m)return -1;
printf("%lld\n",f[n]-ip*m);
return 0;
}
int main(){
read(n),read(m);
for(int i=1;i<=n;i++)read(a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
ll l=0,r=1e12,mid;
while(true){
int tp=che(mid=(l+r)>>1);
if(tp==-1)r=mid-1;
if(tp==1)l=mid+1;
if(!tp)break;
}
return 0;
}
分类:
DP
, 思想
, DP——决策单调性优化(斜率优化、四边形不等式)
, 数据结构——基础数据结构(栈/队列/链表)
, 思想——分治——二分/三分
, 思想——单调性(单调队列/单调栈)
, DP——wqs二分
, 数论——性质的猜想与证明
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?