题意
有一条无限长的公路,公路上依次有 \(n\) 个城市。每经过一个城市可以加 \(g_i\) 升的油,一升油可以行使 \(1\) 单位距离,两个相邻城市之间的距离是 \(w_i\)。出发时油量为所在城市的\(g_i\)。
你需要在 \(l,r\) 之间往返,即需要从 \(l\) 出发向右开到 \(r\),并且从 \(r\) 出发向左开到 \(l\)。
你可以让任意个城市的加油量 \(+x(x>0)\),要求 \(\sum x\le k\)。求最多能在多少个城市之间往返,即最大的 \(r-l+1\)。
数据范围:\(n\le 10^5,w_i,g_i,k\le 10^9\)
solution
记 \(pre_i\) 表示从 \(1\) 出发走到 \(i\),至少要再加多少油。\(suf_i\) 表示从 \(i\) 出发走到 \(1\),至少要再加多少油。
这样我们做差分就可以分别求出 \(l\to r\) 至少要再加多少油和 \(r\to l\) 至少要再加多少油。
现在我们要做的就是尽量使这两部分共用的尽量多。先满足\(l\to r\) ,所需的加油量至少是 \(cost(l,r)=\max_{l<i\le r}\{pre_i\}-pre_l\),贪心地,让 \(l\to r\) 加油的地方一定要越靠右越好。也就是,如果存在一个 \(j\) 满足 \(pre_j>pre_i\),那么最优的方案一定是在 \(g_{j-1}\) 的地方加。这个关系可以使用单调栈维护,并且会形成一个森林。
我们在森林上 DFS,二分出最靠右的一个 \(r\),满足 \(\max_{i<j\le r}\{pre_j\}-pre_i\le k\) ,这个 \(r\) 一定是某个祖先的左边的一个。
现在我们需要只满足 \(r\to l\) ,所以把剩下的部分全部加在 \(g_r\) 上即可。这一部分的代价是 \(suf'_r-\min_{l\le i\le r}\{suf'_i\}\)。这里的 \(suf'\) 指的是在上面的修改 \(g\) 的影响下,重新计算的 \(suf\)。
也就是,在满足了上面二分出的 \(r\) 的条件下,只需要满足 \(cost(l,r)+suf'_r-\min_{l\le i\le r}\{suf'_i\}\le k\) 即可。注意到 \(cost(l,r)\) 造成的修改与 \(suf'_{r}\) 的关系。\(cost(l,r)\) 的修改对 \(suf\) 造成了后缀减,也就是 \(cost(l,r)\) 每 \(+1\),\(suf_r\) 都会 \(-1\)。
需要满足的条件变成
考虑线段树上二分,\(i\le l\) 的限制可以让 \([1,l-1]\) 的部分的 \(suf'\) 变成 \(+\infin\),\(i< r\) 的限制可以让 \([r,n]\) 的部分的 \(suf'\) 变成 \(-\infin\)
现在我们需要的就是维护 \(a_i-\min_{1\le j<i} b_j\) 这个东西,需要支持区间加。
线段树的一个节点维护 \(3\) 个东西
- \(\min suf\),记为 \(mn1\)
- \(\min suf'\),记为 \(mn2\)
- \(\min_{mid<x\le r}\{suf_x-\min_{l\le i< x}\{suf'_i\}\}\),即考虑整个区间的 \(suf'\),\(r\) 只在右子树里取的最小代价。记为 \(val\)
\(update\) 的时候需要 \(O(\log n)\) 在子树里二分。假设当前正在考虑的区间的左侧的 \(\min suf'\) 是 \(mn\),那么
- 如果 \(mn2_{ls}<mn\),那么当前区间左侧的 \(mn2\) 不需要考虑了,答案在 \(val_{id}\) 和 左子树里取 \(\min\)
- 否则,左区间的 \(\min suf'\) 被 \(mn\) 覆盖,答案在 \(mn1_{ls}-mn\) 和右子树里取 \(\min\)
复杂度 \(O(n\log ^2n)\)
线段树上二分的时候,假设当前正在考虑的区间的左侧的 \(\min suf'\) 是 \(mn\),那么
- 如果 \(mn<mn2_{ls}\),\(ls\) 内的条件变为 \(suf_r-mn\le k\),直接二分即可,然后继续考虑右子树(注意每一个线段树上的节点最多会向左二分一次,复杂度也是 \(O(n\log^2n)\))
- 否则与 \(mn\) 无关,根据 \(val_{id}\) 与 \(k\) 的大小关系判断是左子树还是右子树
总时间复杂度 \(O(n\log^2n)\)
view code#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5,inf=1e9;
#define ll long long
const ll Inf=0x3f3f3f3f3f3f3f3f;
int n,g[N],w[N],fa[N];
ll pre[N],suf[N],k;
vector<int> e[N];
#define pb push_back
int stac[N],top;
namespace SGT{
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)>>1)
ll mn1[N<<2],mn2[N<<2],val[N<<2],tag[N<<2];
inline void add(int id,ll a){
mn2[id]+=a;val[id]-=a;tag[id]+=a;
}
inline void pushdown(int id){
add(lid,tag[id]);
add(rid,tag[id]);
tag[id]=0;
}
ll calc(int id,int l,int r,ll mn){
if(l==r)return mn1[id]-mn;
pushdown(id);
if(mn>=mn2[lid])return min(val[id],calc(lid,l,mid,mn));
else return min(mn1[lid]-mn,calc(rid,mid+1,r,mn));
}
inline void update(int id,int l,int r){
mn1[id]=min(mn1[lid],mn1[rid]);
mn2[id]=min(mn2[lid],mn2[rid]);
val[id]=calc(rid,mid+1,r,mn2[lid]);
}
void build(int id,int l,int r){
if(l==r){
mn1[id]=mn2[id]=suf[l];
val[id]=0;
return;
}
build(lid,l,mid);build(rid,mid+1,r);
update(id,l,r);
}
void modify(int id,int l,int r,int L,int R,ll val){
if(L<=l&&r<=R){add(id,val);return;}
pushdown(id);
if(L<=mid)modify(lid,l,mid,L,R,val);
if(R>mid)modify(rid,mid+1,r,L,R,val);
update(id,l,r);
}
int query1(int id,int l,int r,ll lim){
if(l==r)return mn1[id]<=lim?l:0;
pushdown(id);
if(mn1[rid]<=lim)return query1(rid,mid+1,r,lim);
else return query1(lid,l,mid,lim);
}
int query(int id,int l,int r,ll mn){
if(l==r)return mn1[id]-mn<=k?l:0;
pushdown(id);
if(mn<=mn2[lid])
return max(query1(lid,l,mid,k+mn),query(rid,mid+1,r,mn));
else{
if(val[id]<=k)return query(rid,mid+1,r,mn2[lid]);
else return query(lid,l,mid,mn);
}
}
}
int ans=0;
void dfs(int u){
stac[++top]=u;
if(u!=n+1&&fa[u]<=n)
SGT::modify(1,1,n,fa[u]-1,n,pre[u]-pre[fa[u]]);
if(u!=n+1){
int l=1,r=top-1,ret=1;
while(l<=r){
if(pre[stac[mid]]-pre[u]>k)ret=mid,l=mid+1;
else r=mid-1;
}
ret=stac[ret]-1;
if(u>1)SGT::modify(1,1,n,1,u-1,Inf);
SGT::modify(1,1,n,ret,n,-Inf);
ans=max(ans,SGT::query(1,1,n,Inf)-u+1);
if(u>1)SGT::modify(1,1,n,1,u-1,-Inf);
SGT::modify(1,1,n,ret,n,Inf);
}
for(int v:e[u])dfs(v);
if(u!=n+1&&fa[u]<=n)
SGT::modify(1,1,n,fa[u]-1,n,-pre[u]+pre[fa[u]]);
--top;
}
int main(){
n=read();k=read();
for(int i=1;i<n;++i)w[i]=read();
for(int i=1;i<=n;++i)g[i]=read();
for(int i=2;i<=n;++i){
pre[i]=pre[i-1]+w[i-1]-g[i-1];
suf[i]=suf[i-1]+w[i-1]-g[i];
}
pre[n+1]=suf[n+1]=Inf;stac[top=1]=n+1;
for(int i=n;i>=1;--i){
while(pre[stac[top]]<=pre[i])--top;
fa[i]=stac[top];e[stac[top]].pb(i);
stac[++top]=i;
}
top=0;
SGT::build(1,1,n);
dfs(n+1);
printf("%d\n",ans);
return 0;
}