序列「树状数组维护偏序」
题目描述
给定两个长度为 (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)。 数据有一定梯度。
思路分析
- 又是一个求和区间有关的题,二话不说先上前缀和,那么就可以立刻得出一个结论,即 (suma[r]-suma[l-1]>=0) 和 (sumb[r]-sumb[l-1]>=0)
- 将上面的柿子稍微运用小学知识处理一下就是 (suma[r]>=sum[l-1]) 且 (sumb[r]>=sum[l-1]) ,满足这样条件的序列,我们称之为偏序,再加上 (r>=l) ,就成了一个维护三维偏序的题
- 对于 (r>=l) 这一维,由于实际上并不会对答案产生什么实际影响,所以完全可以忽略掉。
- 所以接下来要做的就是在保证另两维其中一个是偏序的情况下,(直接排序即可),再去维护另一维
- 那么另一维怎么维护呢,因为我们还要保证答案最优,所以要让值小的越靠前越好,且由于会对后面的区间均产生影响,所以我们使用树状数组,因为数据太大线段树会炸
- 还有一个细节就是,因为我们所需要的是相对位置,所以如果根据原有值建立树状数组,那树状数组也会吃不消,这时完全可以离散化处理,这样可以保证相对位置不会变
Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000010
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int inf = 0x3f3f3f3f;
int n,size,tr[N],ans=1,a[N],b[N],c[N]; //c用来处理离散化后的sumb
struct node{int a,b,id;}s[N]; //a为suma,b为sumb
inline bool cmp(node x,node y){
return x.a<y.a;
}
inline int lowbit(int x){
return x&(-x);
}
void modify(int x,int v){
while(x<=size){
tr[x] = min(tr[x],v);
x+=lowbit(x);
}
}
int query(int x){
int res = inf;
while(x){
res = min(res,tr[x]);
x-=lowbit(x);
}
return res;
}
signed main(){
n = read();
c[++size] = 0;
for(int i = 1; i <= n; i++) s[i].a = s[i-1].a + read();
for(int i = 1; i <= n; i++) s[i].b = s[i-1].b + read(),s[i].id = i,c[++size] = s[i].b;
for(int i = 1;i <= n;i++){
if(s[i].a>=0&&s[i].b>=0)ans = max(ans,i);
}
sort(c+1,c+size+1);
size = unique(c+1,c+size+1)-(c+1); //离散化
sort(s,s+n+1,cmp);//按a排序,保证a是偏序
memset(tr,0x3f,sizeof(tr));
for(int i = 0; i <= n; i++){
int pos = lower_bound(c+1, c+size+1,s[i].b)-c; //找到相对位置
ans = max(ans, s[i].id - query(pos)); //查询比该值小的最小的位置
modify(pos, s[i].id); //当前位置进行更新
}
printf("%lld
",ans);
return 0;
}