zoukankan      html  css  js  c++  java
  • CF671E(线段树+单调栈)

    传送门

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

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

    [egin{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-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;
    }
    
  • 相关阅读:
    POJ 2486 Apple Tree(树形DP)
    HDOJ 4276 鬼吹灯 (树形DP)
    POJ 2923 Relocation(状态压缩DP)
    Vue,事件的修饰符
    Vye,v-if 和 v-show 显示隐藏的使用
    Vue,for循环的key值绑定
    Vue,v-for循环遍历方式
    Vue-class样式,style样式
    Vue,v-model双向事件绑定 及简易计算器练习
    uni-app 生命周期(下拉已解决)
  • 原文地址:https://www.cnblogs.com/yuanquming/p/12971488.html
Copyright © 2011-2022 走看看