CF671E(线段树+单调栈)
Posted yuanquming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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-minlimits_{lleq i<r} {suf‘_i})leq k)
把(0)和取(max)去掉,可以写成(calc(l,r)+suf‘_r-minlimits_{lleq 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),是一个定值,那么我们只需要考虑后面的那个就行了
对于后面那个,如果(rgeq x),那么(min{suf‘_i})会有一个后缀减的贡献((x-1)及之后的元素)
ps:这就是为什么要定义为左闭右开的形式,如果是闭区间,那么(r=x-1)的时候就会非常麻烦,因为此时不能减,而左闭右开的时候涉及到(x-1)的([l,r])显然有(rgeq x),那么可以直接区间减了
然后我们需要去掉区间的限制,把([1,l-1])加上(infty),而([r_max,n])加上(-infty)即可(这里需要是(r_max),这样才能使(min [l,r](rgeq r_max))=-infty)
然后我们考虑用线段树维护最终的答案,记(ans_p)表示只考虑当前节点的(min{suf‘_i}),且(r)在右子树中,此时最大的答案是多少,转移类似于楼房重建那一题,在子树里(dfs)
具体转移细节看代码,时间复杂度(O(nlog^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
",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
",res);
return 0;
}
以上是关于CF671E(线段树+单调栈)的主要内容,如果未能解决你的问题,请参考以下文章
CF671EOrganizing a Race 单调栈+线段树
CF526F Pudding Monsters 线段树+单调栈
CF997EGood Subsegments (线段树+单调栈)
Codeforces 781E Andryusha and Nervous Barriers 线段树 单调栈