序列 [树状数组+离散化]

序列

题目描述

给定两个长度为n的序列 \(a, b\) 。你需要选择一个区间\([l,r]\),使得 \(a_l+…+a_r\geqslant 0\)\(b_l+…+b_r\geqslant 0\)。最大化你选择的区间长度。

输入格式

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

输出格式

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

样例

样例输入

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

样例输出

1

数据范围与提示

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

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

对于 \(100\%\) 的数据,\(1\leqslant n\leqslant 10^6,|a_i|, |b_i|\leqslant 10^9\)。 数据有一定梯度。

分析

看到题目就能得到三个柿子,也就是个三维偏序:

\[suma_r-suma_l\geqslant 0\\ sumb_r-sumb_l\geqslant 0\\ r-l\geqslant 0\]

但是三个不等式是非常难以维护的,所以我们考虑简化一下这三个式子。

我们可以首先让一个条件满足,我们先按 \(suma\) 的大小升序排序,这样第一个式子就绝对满足了,我们只需要考虑剩下的二维偏序问题。

因为 \(sumb\) 的范围肯定要比 \(long\ long\) 还大,所以我们需要离散化来进行存储。因为我们需要满足在 \(sumb\) 的条件满足的情况下,找到当前这个位置向左拓展的最小的左边界,这个区间的长度就是答案。

根据上边所说的,我们就可以使用树状数组来维护每个 \(sumb\) 的最小左端点,用 \(sumb\) 当作下标(离散化之后的),用当前这个值的位置来更新值,在查询的时候查询这个位置即可,答案就是当前值的位置减去查询到的左端点,取 \(max\) 即可。

代码



#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
ll suma[maxn],sumb[maxn];
struct Node{
	ll a,b,id;
}jl[maxn];
ll n;
inline ll read(){
	ll s = 0,f = 1;
	char ch = getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))s=s*10+ch-'0',ch=getchar();
	return s*f;
}
int t[maxn];
int lowbit(int x){
	return x & -x;
}
void modify(int x,int val){//修改最小值
	while(x <= n){
		t[x] = min(t[x],val);
		x += lowbit(x);
	}
}
int query(int x){//查询最小左端点
	int res = 0x3f3f3f3f;
	while(x){
		res = min(res,t[x]);
		x -= lowbit(x);
	}
	return res;
}
bool cmp(Node x,Node y){//按suma排序
	return x.a < y.a;
}
int main(){
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
	memset(t,0x3f,sizeof(t));//因为要维护最小的左端点,所以初始化极大值
	n = read();
	for(register int i = 1;i <= n;++i){
		ll x = read();
		jl[i].id = i;
		suma[i] = suma[i-1] + x;
		jl[i].a = suma[i];
	}
	for(register int i = 1;i <= n;++i){
		ll x = read();
		sumb[i] = sumb[i-1] + x;
		jl[i].b = sumb[i];
	}
	int ans = 1;
	for(register int i = 1;i <= n;++i){
		if(sumb[i] >= 0 && suma[i] >= 0){//先提前处理一下,不然会挂
			ans = max(ans,i);
		}
	}
	sort(jl+1,jl+n+1,cmp);//按suma排序,去掉一个限制条件
	//以下两行离散化
	sort(sumb+1,sumb+n+1);
	int q = unique(sumb+1,sumb+n+1)-sumb-1;
	for(int i=1;i<=n;++i){
		int pos = lower_bound(sumb+1,sumb+q+1,jl[i].b) - sumb;//找到这个sumb的位置(也就是离散化后的值)
		modify(pos,jl[i].id);//修改
		ans = max((ll)ans,jl[i].id - query(pos));//查询并更新答案
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-08-14 20:33  Vocanda  阅读(247)  评论(1编辑  收藏  举报