(线段树)SP2916 GSS5 / nfls #2899 查询最大子段和 题解

题意

(nflsoj 数据以及实际数据) n , m ≤ 1 0 5 n,m\le 10^5 n,m≤105, a i ∈ − 1 0 4 , 1 0 4 a_i\in-10\^4,10\^4 ai∈−104,104

思路

如果区间不交,即 r 1 < l 2 r_1<l_2 r1<l2,那么就把前缀和扔上线段树,在左区间查最小值、右区间找最大值,相减即可。也可以维护一段区间的最大前缀和 l m a lma lma、最大后缀和 r m a rma rma,钦定 l m a , r m a lma,rma lma,rma 至少有一个元素。答案就是 l m a l 1 , r 1 + s u m ( r 1 , l 2 ) + r m a l 2 , r 2 lma_{l_1,r_1}+sum_{(r_1,l_2)}+rma_{l_2,r_2} lmal1,r1+sum(r1,l2)+rmal2,r2

但是这里会出现区间有交的情况,本题的考点就是这里的分类讨论,一共有 4 4 4 种情况以及细节,对于答案区间 x , y x,y x,y

  • x , y ∈ l 2 , r 1 x,y\inl_2,r_1 x,y∈l2,r1:答案为区间 l 2 , r 1 l_2,r_1 l2,r1 的最大子段和,维护方法同 GSS1
  • x ∈ l 1 , l 2 , y ∈ l 2 , r 1 x\inl_1,l_2,y\inl_2,r_1 x∈l1,l2,y∈l2,r1:答案为 r m a l 1 , l 2 + l m a l 2 , r 1 − a l 2 rma_{l_1,l_2}+lma_{l_2,r_1}-a_{l_2} rmal1,l2+lmal2,r1−al2;
  • x ∈ l 2 , r 1 , y ∈ r 1 , r 2 x\inl_2,r_1,y\inr_1,r_2 x∈l2,r1,y∈r1,r2:答案为 r m a l 2 , r 1 + l m a r 1 , r 2 − a r 1 rma_{l_2,r_1}+lma_{r_1,r_2}-a_{r_1} rmal2,r1+lmar1,r2−ar1;
  • x ∈ l 1 , l 2 , y ∈ r 1 , r 2 x\inl_1,l_2,y\inr_1,r_2 x∈l1,l2,y∈r1,r2:答案为 r m a l 1 , l 2 + s u m ( l 2 , r 1 ) + l m a r 1 , r 2 − l 2 = r 1 a l 2 rma_{l_1,l_2}+sum_{(l_2,r_1)}+lma_{r_1,r_2}-l_2=r_1a_{l_2} rmal1,l2+sum(l2,r1)+lmar1,r2−l2=r1al2。

第 4 4 4 种情况要考虑 l 2 = r 1 l_2=r_1 l2=r1 的情况。画个图会好理解。

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ls u<<1
#define rs u<<1|1
const ll N=1e5+9,inf=3e14;
ll T,n,Q;
ll a[N];
struct SegT
{
	struct node
	{
		ll sum;
		ll lma,rma,ans;
	}T[N<<2];
	node add(node a,node b)
	{
		node ret;
		ret.sum=a.sum+b.sum;
		ret.lma=max(a.lma,a.sum+b.lma);
		ret.rma=max(b.rma,b.sum+a.rma);
		ret.ans=max(max(a.ans,b.ans),a.rma+b.lma);
		return ret;
	}
	void pushup(ll u)
	{
		T[u]=add(T[ls],T[rs]);
	}
	void build(ll u,ll l,ll r)
	{
		T[u]=(node){-inf,-inf,-inf,-inf};
		if(l==r)
		{
			T[u].sum=T[u].ans=T[u].lma=T[u].rma=a[l]; 
			return;
		}
		ll mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		pushup(u);
	}
	node get_info(ll u,ll l,ll r,ll ql,ll qr)
	{
		if(qr<ql)return {0,0,0,0};
		if(ql<=l&r<=qr)return T[u];
		ll mid=(l+r)>>1;
		if(qr<=mid)return get_info(ls,l,mid,ql,qr);
		if(ql>mid)return get_info(rs,mid+1,r,ql,qr);
		return add(get_info(ls,l,mid,ql,qr),get_info(rs,mid+1,r,ql,qr));
		//返回结构体,条件反转,add函数在结构体之间运算 
	}
}A;
ll query(ll l1,ll r1,ll l2,ll r2)
{
	if(r1<l2)return A.get_info(1,1,n,l1,r1).rma
					+A.get_info(1,1,n,r1+1,l2-1).sum
					+A.get_info(1,1,n,l2,r2).lma;
	return max(max(A.get_info(1,1,n,l2,r1).ans,
					A.get_info(1,1,n,l1,l2).rma+A.get_info(1,1,n,l2+1,r1-1).sum+A.get_info(1,1,n,r1,r2).lma-(l2==r1?a[l2]:0)),
				max(A.get_info(1,1,n,l1,l2).rma+A.get_info(1,1,n,l2,r1).lma-a[l2],
					A.get_info(1,1,n,l2,r1).rma+A.get_info(1,1,n,r1,r2).lma-a[r1]));
}
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld",&n);
		for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
		A.build(1,1,n);
		scanf("%lld",&Q);
		while(Q--)
		{
			ll l1,r1,l2,r2;
			scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
			printf("%lld\n",query(l1,r1,l2,r2));
		}
	}
	return 0;
}