给定一个长度为
n
n
的序列 ,以及
q
q
次询问,每次询问给定 两参数。
对于每次询问,求
al
a
l
到
ar
a
r
之间的最大子段和,子段的意思是连续非空子区间。
更形式化地解释:对于每次询问给定的
l,r
l
,
r
,
求一个整数
ans
a
n
s
,使得存在整数
l′,r′
l
′
,
r
′
,满足
l≤l′≤r′≤r
l
≤
l
′
≤
r
′
≤
r
且
∑r′i=l′ai=ans
∑
i
=
l
′
r
′
a
i
=
a
n
s
。
其中,
n,q≤200000
n
,
q
≤
200000
,
ai
a
i
在
int
i
n
t
范围内但不保证答案在
int
i
n
t
范围内。
看题就知道要数据结构啦,
我们直接考虑合并,对于某个区间,它的两个左右儿子区间怎样合并为这个区间?
首先对于每个节点,我们肯定要记录这个区间的最大子段和,记作
gss
g
s
s
。但是如果直接把两个儿子的最大子段和取最大一定是错的,因为一个区间的最大子段可能会跨过左右区间的分界点,而不是单独分布在左区间或者右区间。
为了维护这种情况,我们得多记两个信息:
以该区间左/右端点为起点的最大子段和,分别记作
lgss,rgss
l
g
s
s
,
r
g
s
s
。
如果能够维护,那么上述的另一种情况就也能被我们维护下来。我们只要把左区间的
rgss
r
g
s
s
加上右区间的
lgss
l
g
s
s
,就能得出跨分界点的最大子段和。
但我们在合并时要怎样维护
lgss
l
g
s
s
和
rgss
r
g
s
s
呢?由于两个信息是对称的,我们拿
lgss
l
g
s
s
为例。
大区间的
lgss
l
g
s
s
一定等于左区间的
lgss
l
g
s
s
吗?不一定。因为可能跨过分界点。
如果跨过分界点,其就是左区间的所有元素和加上右区间的
lgss
l
g
s
s
。所以我们还得在维护一个区间和,这个非常好维护。
因此这就是合并了,放个代码:
void Merge(R node &fa,R node &s1,R node &s2)
{
fa.gss=max(max(s1.gss,s2.gss),s1.rgss+s2.lgss);
fa.lgss=max(s1.lgss,s1.sum+s2.lgss);
fa.rgss=max(s2.rgss,s2.sum+s1.rgss);
fa.sum=s1.sum+s2.sum;
}
(区间
s1
s
1
与
s2
s
2
合并为
fa
f
a
。)
理一理思路:
合并弄清楚了其它都不难。询问就把询问区间拆成线段树上的区间然后逐个合并即可。复杂度
O(n log n)
O
(
n
l
o
g
n
)
。
完整代码:
#include <bits/stdc++.h>
using namespace std;
#define R register
#define Maxn 200005
#define LL long long
#define lson (now<<1)
#define rson (lson|1)
#define inf -9999999999999999
int n,q;
struct node
{
LL gss,lgss,rgss,sum;
node(){gss=lgss=rgss=sum=inf;return;}
}T[Maxn<<2];
void Merge(R node &fa,R node &s1,R node &s2)
{
fa.gss=max(max(s1.gss,s2.gss),s1.rgss+s2.lgss);
fa.lgss=max(s1.lgss,s1.sum+s2.lgss);
fa.rgss=max(s2.rgss,s2.sum+s1.rgss);
fa.sum=s1.sum+s2.sum;
}
void Build(R int now,R int l,R int r)
{
if(l==r)
{
scanf("%lld",&T[now].gss);
T[now].lgss=T[now].rgss=T[now].sum=T[now].gss;
return ;
}
R int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
Merge(T[now],T[lson],T[rson]);
}
int ql,qr;
bool key;
node ans;
void Query(R int now,R int l,R int r)
{
if(ql<=l && qr>=r)
{
if(key) Merge(ans,ans,T[now]);
else ans=T[now],key=1;
return;
}
R int mid=(l+r)>>1;
if(mid >= ql)Query(lson,l,mid);
if(mid < qr)Query(rson,mid+1,r);
}
int main()
{
scanf("%d",&n);
Build(1,1,n);
scanf("%d",&q);
for (R int i=1;i<=q;++i)
{
scanf("%d %d",&ql,&qr);
key=0;Query(1,1,n);
printf("%lld\n",ans.gss);
}
return 0;
}
因篇幅问题不能全部显示,请点此查看更多更全内容