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

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

题目描述

给定两个长度为 (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;
}

原文地址:https://www.cnblogs.com/hhhhalo/p/13507391.html