CF671E(线段树+单调栈)
神仙题,看题解看了一个多小时才看懂
首先我们设\(Pre_i\)和\(suf_i\)分别表示\(1\)到\(i\)需要的额外油量和\(i\)到\(1\)需要的额外油量,那么有
从\(i\)向右走到\(j\)需要的额外油量是\(Pre_j-Pre_i\),从\(j\)向左走到\(i\)需要的额外油量是\(suf_j-suf_i\)
对于每个\(i\),我们算出\(nxt_i=\min{k|Pre_k-Pre_i>0}\),即往右走时第一个走不到的位置,用单调栈就可以处理。从\(nxt_i\)向\(i\)连一条边,最终可以形成一棵树
考虑如果要从\(i\)走到\(nxt_i\),那么需要的最少油量是\(Pre_{nxt_i}-Pre_i\),而由于我们之后还需要从右往左走回来,所以这些油量肯定是加到\(a_{nxt_i-1}\)上是最优的,而对于\(i\)来说,它右边需要加油量的所有位置,就是它在树上的祖先的那些位置,所以我们可以在\(dfs\)这棵树的时候,每次进入一个子树就加入贡献,出去就减掉贡献
设从\(l\)走到\(r\)的最小代价是\(calc(l,r)\),那么一个\(r\)合法,首先需要满足\(calc(l,r)\leq k\)
由于我们加油量会对\(suf\)造成更改,我们设\(suf'\)表示更改后的\(suf\)
然后在考虑从\(r\)向左走到\(l\),剩下的油量肯定是全都加到\(r\)位置上最优,所以还需要满足\(calc(l,r)+\max(0,suf'_r-\min\limits_{l\leq i<r} \{suf'_i\})\leq k\)
把\(0\)和取\(\max\)去掉,可以写成\(calc(l,r)+suf'_r-\min\limits_{l\leq i<r} \{suf'_i\}\leq k\)
假设我们已经固定了\(l\),考虑如何计算最大的满足条件的\(r\)
首先因为\(calc(l,r)\leq k\),而\(calc(l,r)\)单调不降,所以我们需要先二分求出\(r_\max\)
令\(x=nxt_i\),我们注意到,每次进入一棵子树的时候都会对上面的值有影响
对于\(cost(l,r)\),会产生一个后缀加的贡献(\(x\)及之后的元素)
对于\(suf'_r\),会产生一个后缀减的贡献(\(x\)及之后的元素)
而且我们可以发现,它们后缀加后缀减的值是一样的!所以\(cost(l,r)+suf'_r=suf_r\),是一个定值,那么我们只需要考虑后面的那个就行了
对于后面那个,如果\(r\geq x\),那么\(\min\{suf'_i\}\)会有一个后缀减的贡献(\(x-1\)及之后的元素)
ps:这就是为什么要定义为左闭右开的形式,如果是闭区间,那么\(r=x-1\)的时候就会非常麻烦,因为此时不能减,而左闭右开的时候涉及到\(x-1\)的\([l,r]\)显然有\(r\geq x\),那么可以直接区间减了
然后我们需要去掉区间的限制,把\([1,l-1]\)加上\(\infty\),而\([r_\max,n]\)加上\(-\infty\)即可(这里需要是\(r_\max\),这样才能使\(\min [l,r](r\geq r_\max)\)=-\infty)
然后我们考虑用线段树维护最终的答案,记\(ans_p\)表示只考虑当前节点的\(\min\{suf'_i\}\),且\(r\)在右子树中,此时最大的答案是多少,转移类似于楼房重建那一题,在子树里\(dfs\)
具体转移细节看代码,时间复杂度\(O(n\log^2 n)\)
//yuanquming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
inline ll min(R ll x,R ll y){return x<y?x:y;}
inline ll max(R ll x,R ll y){return x>y?x:y;}
const int N=1e5+5;const ll inf=1e18;
ll Pre[N],suf[N],ans[N<<2],amn[N<<2],bmn[N<<2],t[N<<2];
int a[N],w[N],st[N],nxt[N],n,k,top,res;
#define lc (p<<1)
#define rc (p<<1|1)
#define ls lc,l,mid
#define rs rc,mid+1,r
inline void ppd(R int p,R ll x){bmn[p]+=x,ans[p]-=x,t[p]+=x;}
inline void pd(R int p){if(t[p])ppd(lc,t[p]),ppd(rc,t[p]),t[p]=0;}
ll calc(int p,int l,int r,ll x){
if(l==r)return amn[p]-x;
int mid=(l+r)>>1;pd(p);
if(bmn[lc]<x)return min(calc(ls,x),ans[p]);
return min(amn[lc]-x,calc(rs,x));
}
void build(int p,int l,int r){
if(l==r)return amn[p]=bmn[p]=suf[r],void();
int mid=(l+r)>>1;
build(ls),build(rs);
amn[p]=min(amn[lc],amn[rc]),bmn[p]=min(bmn[lc],bmn[rc]);
ans[p]=calc(rs,bmn[lc]);
}
void update(int p,int l,int r,int ql,int qr,ll v){
if(ql<=l&&qr>=r)return ppd(p,v);
int mid=(l+r)>>1;pd(p);
if(ql<=mid)update(ls,ql,qr,v);
if(qr>mid)update(rs,ql,qr,v);
bmn[p]=min(bmn[lc],bmn[rc]);
ans[p]=calc(rs,bmn[lc]);
}
ll qr(int p,int l,int r,ll x){
if(l==r)return r;
int mid=(l+r)>>1;
return amn[rc]<=x?qr(rs,x):qr(ls,x);
}
ll query(int p,int l,int r,ll &x){
if(l==r){
int ret=amn[p]-x<=k?r:0;
cmin(x,bmn[p]);
return ret;
}
int mid=(l+r)>>1;pd(p);
if(bmn[lc]<x){
if(ans[p]<=k)return query(rs,x=bmn[lc]);
int ret=query(ls,x);
cmin(x,bmn[p]);
return ret;
}
int ret=amn[lc]<=k+x?qr(ls,k+x):0;
return max(ret,query(rs,x));
}
#undef lc
#undef rc
#undef mid
#undef ls
#undef rs
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
void init(){
res=1;
fp(i,2,n){
Pre[i]=Pre[i-1]+w[i-1]-a[i-1];
suf[i]=suf[i-1]+w[i-1]-a[i];
}
nxt[n+1]=st[top=0]=n+1,Pre[n+1]=inf;
fd(i,n,1){
while(top&&Pre[i]>=Pre[st[top]])--top;
nxt[i]=st[top],st[++top]=i;
// to[nxt[i]].pb(i);
add(nxt[i],i);
}
top=0;
}
void dfs(int u){
// printf("Now is in %d %d %lld %lld\n",u,nxt[u],Pre[u],suf[u]);
st[++top]=u;
if(nxt[u]<=n)update(1,1,n,nxt[u]-1,n,Pre[u]-Pre[nxt[u]]);
if(u<=n){
int l=2,r=top-1,mid,ret=1;
while(l<=r){
mid=(l+r)>>1;
Pre[st[mid]]-Pre[u]>k?(ret=mid,l=mid+1):r=mid-1;
}
int rmx=st[ret]-1;ll mn=inf;
if(u>1)update(1,1,n,1,u-1,inf);
update(1,1,n,rmx,n,-inf);
int pos=query(1,1,n,mn);
if(u>1)update(1,1,n,1,u-1,-inf);
update(1,1,n,rmx,n,inf);
cmax(res,pos-u+1);
}
// for(auto v:to[u])dfs(v);
go(u)dfs(v);
if(nxt[u]<=n)update(1,1,n,nxt[u]-1,n,Pre[nxt[u]]-Pre[u]);
--top;
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d%d",&n,&k);
fp(i,1,n-1)scanf("%d",&w[i]);
fp(i,1,n)scanf("%d",&a[i]);
init();
build(1,1,n);
dfs(n+1);
printf("%d\n",res);
return 0;
}