序列 [树状数组+离散化]
序列
题目描述
给定两个长度为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;
}
$Never\ Give\ Up$