正题

题目链接:https://www.ybtoj.com.cn/problem/463


题目大意

给出长度为\(n\)的序列\(A,B\)。要求划分成若干段满足

  1. 对于任何\(i<j\),若\(i\)和\(j\)不是同一段的,要求满足\(B_i>A_j\)
  2. 每一段\(A_i\)的最大值的和不能超过\(m\)

要求最小化每一段\(B_i\)和的最大值。

\(n\in[1,10^5],A_i,B_i\in[1,10^9],m\in[1,10^{12}]\)


解题思路

最大值最小化很显然直接二分,然后变为求每一段\(A_i\)最大值的和的最小值。

第一个条件相当于限制了什么位置能够作为划分段的末尾,求一个前缀\(min\{b_i\}\)和一个后缀\(max\{a_i\}\)能够快速求出这些位置。

然后考虑\(dp\),转移方程就是

\[f_i=min\{f_j+max\{a_k\}(\ k\in(j,i]\ )\}
\]

二分的条件限制了\(j\)的范围,加个指针就好了

这个东西好像很难搞,但是注意到\(v_j=max\{a_k\}\)这一部分是递减的,并且每次会让所有\(v_i\)的一起和一个一起取\(max\)。

因为是递减的,所以每次加入一个新的就相当于修改一段后缀的\(v_i\),然后求一个区间的最大\(f_i+v_i\)了。

可以线段树维护,每个节点维护该区间最大的\(f_i+v_i\)和最大的\(f_i\)。区间推平\(v_i\)的时候就可以拿最大的\(f_i\)来更新\(f_i+v_i\)

时间复杂度\(O(n\log n\log\sum b_i)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10,inf=1e9+7;
ll n,m,a[N],b[N],pre[N],suf[N],last[N];
ll lg[N],st[N][17],v[N<<2],w[N<<2],lazy[N<<2];
void Downdata(ll x){
if(!lazy[x])return;
lazy[x*2]=lazy[x*2+1]=lazy[x];
w[x*2]=v[x*2]+lazy[x];
w[x*2+1]=v[x*2+1]+lazy[x];
lazy[x]=0;return;
}
void Changew(ll x,ll L,ll R,ll l,ll r,ll val){
if(l>r)return;
if(L==l&&R==r){w[x]=v[x]+val;lazy[x]=val;return;}
ll mid=(L+R)>>1;Downdata(x);
if(r<=mid)Changew(x*2,L,mid,l,r,val);
else if(l>mid)Changew(x*2+1,mid+1,R,l,r,val);
else Changew(x*2,L,mid,l,mid,val),Changew(x*2+1,mid+1,R,mid+1,r,val);
w[x]=min(w[x*2],w[x*2+1]);
}
void Changev(ll x,ll l,ll r,ll pos,ll val){
if(l==r){v[x]=val;w[x]=v[x]+lazy[x];return;}
ll mid=(l+r)>>1;Downdata(x);
if(pos<=mid)Changev(x*2,l,mid,pos,val);
else Changev(x*2+1,mid+1,r,pos,val);
w[x]=min(w[x*2],w[x*2+1]);
v[x]=min(v[x*2],v[x*2+1]);
return;
}
ll Ask(ll x,ll L,ll R,ll l,ll r){
if(L==l&&R==r)return w[x];
ll mid=(L+R)>>1;Downdata(x);
if(r<=mid)return Ask(x*2,L,mid,l,r);
if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
return min(Ask(x*2,L,mid,l,mid),Ask(x*2+1,mid+1,R,mid+1,r));
}
ll RMQ(ll l,ll r){
ll z=lg[r-l+1];
return max(st[l][z],st[r-(1<<z)+1][z]);
}
bool check(ll x){
memset(v,0x3f,sizeof(v));
memset(w,0x3f,sizeof(w));
memset(lazy,0,sizeof(lazy));
ll sum=0,l=0,tmp=v[0];
Changev(1,0,n,0,0);
for(ll i=1;i<=n;i++){
sum+=b[i];
while(sum>x)l++,sum-=b[l];
Changew(1,0,n,last[i],i-1,a[i]);
if(pre[i]<=suf[i+1])continue;
tmp=Ask(1,0,n,l,i);
Changev(1,0,n,i,tmp);
}
return (tmp<=m);
}
signed main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%lld%lld",&n,&m);
ll l=1,r=0;pre[0]=inf;
for(ll i=1;i<=n;i++)
scanf("%lld%lld",&a[i],&b[i]),r+=b[i],l=max(l,b[i]),st[i][0]=a[i];
for(ll i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(ll j=1;(1<<j)<=n;j++)
for(ll i=1;i+(1<<j)-1<=n;i++)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
for(ll i=1;i<=n;i++){
ll l=1,r=i-1;
while(l<=r){
ll mid=(l+r)>>1;
if(RMQ(mid,i)>a[i])l=mid+1;
else r=mid-1;
}
last[i]=r;
}
for(ll i=1;i<=n;i++)
pre[i]=min(pre[i-1],b[i]);
for(ll i=n;i>=1;i--)
suf[i]=max(suf[i+1],a[i]);
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid))r=mid-1;
else l=mid+1;
}
check(l+1);
printf("%lld\n",l);
}

最新文章

  1. [BZOJ2429][HAOI2006]聪明的猴子(MST)
  2. 数学符号“s.t.”的意义
  3. [jQuery学习系列四 ]4-Jquery学习四-事件操作
  4. php文件删除unlink()详解
  5. jMeter 监控cpu、内存
  6. 数据挖掘R与SQL
  7. SQL Server 2005中的分区表(三):将普通表转换成分区表
  8. android开发关于和使用本机内存、内置存储卡和外置存储卡 (转)
  9. mysql 调用存储过程及例子
  10. 深入理解javascript执行上下文(Execution Context)
  11. pyqt5 动画学习(三) 指定控件的移动轨迹
  12. Hibernate查询以及优化策略04
  13. 2018.12.14 浪在ACM 集训队第九次测试赛
  14. 下载神器(vip下载速度)
  15. MatConvNet+Matlab2017a+CUDA8.0安装
  16. AngularJS的基础知识
  17. Java基础—IO小结(二)缓冲流与其它流的使用
  18. python开发mysql:索引
  19. jQuery使用toggle()方法进行显示隐藏
  20. MySQL备份恢复之mysqldump

热门文章

  1. Java:学习什么是多线程
  2. ASP.NET Core教程:ASP.NET Core使用AutoMapper
  3. The Programmer's Oath程序员的誓言----鲍勃·马丁大叔(Bob Martin)
  4. (3)hadoop单节点配置
  5. 接上一篇安装linux问题,解决redis安装后make test错误
  6. jquery mobile常用的data-role类型
  7. k8s笔记0528-基于KUBERNETES构建企业容器云手动部署集群记录-6
  8. Servlet监听器详解及举例
  9. golang net/http包
  10. Tensorflow之TFRecord的原理和使用心得