CF671E Organizing a Race

原发布于2020-01-14。

upd 2020-04-10: 代码写错了,被hack了,已更正

link

网上大多数都是O(nlog2n)的,不过我做法是O(nlogn)的...

题解

首先考虑没有k限制的情况(或者说加完之后)如何判断l,r是否可以办比赛。

将题意中的giai表示,wibi表示。

fi=fi1+ai1bi1,gi=gi1+bi1ai

那么l,r可以办比赛当且仅当 (1)fl=minlirfi,gr=minlirgi

那么考虑有k限制的情况。

如果ai+1,那么对于i<j,fj=fj+1,对于ij,gj=gj1

n1枚举l,设单调栈为fl>fx1>fx2>fx3(l<x1<x2<x3)

那么为了lr满足条件,肯定是axi1=axi1+fxi1fxi对于i升序一个一个执行直到l能到r,其中x0=l

接下来考虑rl

hi为做完axi1=axi1+fxi1fxi后的gi,

显然剩下的ai增加都要给ar,那么r能到l当且仅当grkhi,li<r

如果对于il定义hi=,那么就是grkhi,i<r

可以在单调栈二分出r的上界,然后在线段树上二分得到最大可行r

时间复杂度O(nlogn)

代码

define int long long

int a[100005],b[100005],f[100005],g[100005];
int s[100005],t;
int n,k;
struct smt{
	int amn,bmn,atg;	
	int ls,rs;
	smt *l,*r;
	smt(int la,int ra){
		amn=inf;bmn=-inf;
		atg=0;
		ls=la;rs=ra;
		if(ls==rs){
			l=r=0;
		}
		else{
			int mid=(ls+rs)>>1;
			l=new smt(ls,mid);
			r=new smt(mid+1,rs);
		}
	}
	void setg(int x){
		if(ls==rs){
			amn=bmn=g[x];	
			return;
		}
		if(x<=l->rs)l->setg(x);
		else r->setg(x);
		amn=min(l->amn,r->amn);
		bmn=min(l->bmn,r->bmn);
	}
	void push_down(){
		l->atg+=atg;
		l->amn+=atg;
		r->atg+=atg;
		r->amn+=atg;
		atg=0;
	}
	void add(int la,int ra,int w){
		if(la<=ls && rs<=ra){
			atg+=w;amn+=w;
			return;
		}
		push_down();
		if(la<=l->rs)l->add(la,ra,w);
		if(ra>=r->ls)r->add(la,ra,w);
		amn=min(l->amn,r->amn);
	}
	int query(int x,int rm){
		if(ls>rm)return 0;
		if(bmn-k>x)return 0;//没加这一句话会错
		if(ls==rs)return ls;
		push_down();
		if(min(x,l->amn)>=r->bmn-k){
			int ans=r->query(min(x,l->amn),rm);
			if(ans)return ans;
		}
		return l->query(x,rm);
	}
};
smt *rt;
signed main(){
#ifdef QAQAutoMaton 
	freopen("E.in","r",stdin);
	freopen("E.out","w",stdout);
#endif
	read(n,k);
	rt=new smt(1,n);
	for(int i=1;i<n;++i)read(b[i]);
	for(int i=1;i<=n;++i)read(a[i]);
	for(int i=1;i<=n;++i){
		f[i]=f[i-1]+a[i-1]-b[i-1];
	}
	for(int i=1;i<=n;++i){
		g[i]=g[i-1]+b[i-1]-a[i];
	}
	int ans=0;
	for(int i=n;i;--i){
		rt->setg(i);
		while(t && f[s[t]]>=f[i]){
			if(t>1){
				rt->add(s[t-1]-1,n,f[s[t]]-f[s[t-1]]);
			}
			--t;
		}
		s[++t]=i;
		if(t>1){
			rt->add(s[t-1]-1,n,f[s[t-1]]-f[s[t]]);
		}

		s[0]=n+1;

		int l=1,r=t,mid;
		while(l<=r){
			mid=(l+r)>>1;
			if(f[i]-f[s[mid]]<=k)r=mid-1;
			else l=mid+1;
		}
		chkmax(ans,rt->query(inf,s[r]-1)-i+1);
	}
	write(ans,'\n');
	return 0;
}