金题大战Vol.0 B、序列 树状数组

金题大战Vol.0 B、序列

题目描述

给定两个长度为 \(n\) 的序列\(a\), \(b\)

你需要选择一个区间\([l,r]\),使得\(a_l+…+a_r>=0\)\(b_l+…+b_r>=0\)。最大化你选择的区间长度。

输入格式

第一行一个整数\(n\),第二行\(n\)个整数\(a_1-a_n\),第三行n个整数\(b_1-b_n\)

输出格式

一行一个整数表示\(max(r-l+1)\)。保证至少有一个区间满足条件。

样例

样例输入

5
2 -4 1 2 -2
-2 3 1 -3 1

样例输出

1

数据范围与提示

对于\(20\%\) 的数据,\(n<=5000\)

对于\(60\%\) 的数据,\(n<=10^5\)

对于\(100\%\) 的数据,\(1<=n<=10^6,|ai|, |bi|<=10^9\)。 数据有一定梯度。

分析

乍看上去这一道题似乎不太好处理,要同时满足下标、\(a\)\(b\)三个条件

突破点就在于怎么把限制条件一维一维地删去

首先我们把题目中给出的数组转化成前缀和的形式

\(suma[r]>=suma[l],sumb[r]>=sumb[l]\)

我们将 \(suma\) 从小到大排一下序

这样我们每一次从左到右遍历,就相当于消去了一维

我们只考虑 \(sumb\) 和坐标的关系即可

这种关系我们可以用树状数组去维护,即把 \(sumb\) 的值作为树状数组的下标,把原先的编号作为树状数组的权值

这样在每次遇到一个点时,我们在树状数组中查询 \(sumb\) 比它小的最小的下标

同时更新下标为 \(sumb\) 的节点的值为当前点的编号

\(sumb\) 比较大,并且有负数,因此考虑离散化

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int a[maxn],b[maxn],n,cnt,tr[maxn];
ll suma[maxn],sumb[maxn];
struct asd{
	int wz;
	ll jla,jlb;
}jl[maxn];
bool cmp(asd aa,asd bb){
	return aa.jla<bb.jla;
}
int lb(int xx){
	return xx&-xx;
}
void ad(int wz,int val){
	for(int i=wz;i<=n;i+=lb(i)){
		tr[i]=min(tr[i],val);
	}
}
int cx(int wz){
	int ans=0x3f3f3f3f;
	for(int i=wz;i>0;i-=lb(i)){
		ans=min(ans,tr[i]);
	}
	return ans;
}
int main(){
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
	int ans=1,bef,wz;
	memset(tr,0x3f,sizeof(tr));
	n=read();
	for(register int i=1;i<=n;i++){
		a[i]=read();
		suma[i]=suma[i-1]+(ll)a[i];
	}
	for(register int i=1;i<=n;i++){
		b[i]=read();
		sumb[i]=sumb[i-1]+(ll)b[i];
	}
	for(register int i=1;i<=n;i++){
		jl[i].jla=suma[i];
		jl[i].jlb=sumb[i];
		if(jl[i].jla>=0 && jl[i].jlb>=0){
			ans=max(ans,i);
		}
		jl[i].wz=i;
	}
	sort(jl+1,jl+1+n,cmp);
	sort(sumb+1,sumb+1+n);
	cnt=unique(sumb+1,sumb+1+n)-sumb-1;
	for(int i=1;i<=n;i++){
		wz=lower_bound(sumb+1,sumb+cnt+1,jl[i].jlb)-sumb;
		bef=cx(wz);
		if(bef>=jl[i].wz){
			ad(wz,jl[i].wz);
			continue;
		}
		ans=max(ans,jl[i].wz-bef);
		ad(wz,jl[i].wz);
	}
	printf("%d\n",ans);
	return 0;
}

posted @ 2020-08-14 20:20  liuchanglc  阅读(184)  评论(0编辑  收藏  举报