序列「树状数组维护偏序」

序列「树状数组维护偏序」

题目描述

给定两个长度为 \(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\n",ans);
	return 0;
}

posted @ 2020-08-15 06:23  HH_Halo  阅读(184)  评论(0编辑  收藏  举报