码迷,mamicode.com
首页 > 其他好文 > 详细

CF671E(线段树+单调栈)

时间:2020-05-27 12:17:56      阅读:64      评论:0      收藏:0      [点我收藏+]

标签:can   update   题解   http   div   span   line   mit   位置   

传送门

神仙题,看题解看了一个多小时才看懂

首先我们设\(Pre_i\)\(suf_i\)分别表示\(1\)\(i\)需要的额外油量和\(i\)\(1\)需要的额外油量,那么有

\[\begin{aligned} Pre_i=Pre_{i-1}-a_{i-1}+w_{i-1}\suf_i=suf_{i-1}-a_i+w_{i-1} \end{aligned} \]

\(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;
}

CF671E(线段树+单调栈)

标签:can   update   题解   http   div   span   line   mit   位置   

原文地址:https://www.cnblogs.com/yuanquming/p/12971488.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!