Steven_MengのBlog

传送门
线段树每个节点中存的值:

$val[]$ ——区间所有数之和
$lmax[]$ ——选的$a[i]$在区间内,且包括$a[l]$,连续$a[i]$之和的最大值
$rmax[]$ ——选的$a[i]$在区间内,且包括$a[r]$,连续$a[i]$之和的最大值
$maxn[]$ ——选的$a[i]$在区间内,连续$a[i]$之和的最大值(即本题答案)
之所以要计算$lmax$,$rmax$,$val$,是为了合并节点时,能计算出$maxn$的值


这里介绍一种比较简洁的写法
这道题中线段树的重点是节点合并,即$pushup$的过程
不妨将节点合并的操作定义为+
这样Query时就不用再敲一遍相同的代码,直接将查询结果相”+”即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <bits/stdc++.h>
#define MAXN 50005
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^'0');
ch=getchar();
}
return x*f;
}
int a[MAXN];
namespace SegmentTree{
struct node{
int l,r;
int lmax,rmax,maxn;
//从左端开始最大值,从右端开始最大值,整段最大值
int val;//这段的和
}tree[MAXN<<2];
#define lc i<<1
#define rc i<<1|1
node operator + (node A,node B){//定义合并操作+
node temp;
temp.lmax=max(B.lmax+A.val,A.lmax);
//可以左区间全部选,加上右区间lmax,也可以直接用左区间lmax
temp.rmax=max(A.rmax+B.val,B.rmax);
//同理
temp.maxn=max(max(A.maxn,B.maxn),A.rmax+B.lmax);
//可以直接用左右区间maxn,也可以将左右区间rmax,lmax合并
temp.val=A.val+B.val;
temp.l=A.l,temp.r=B.r;
return temp;
}
void pushup(int i){
tree[i]=tree[lc]+tree[rc];
}
void Build(int l,int r,int i){//建树
if (l==r){
tree[i].lmax=tree[i].rmax=tree[i].maxn=tree[i].val=a[l];
tree[i].l=tree[i].r=l;
return ;
}
int mid=(l+r)>>1;
Build(l,mid,lc);
Build(mid+1,r,rc);
pushup(i);
}
node Query(int L,int R,int i){
if (L<=tree[i].l&&tree[i].r<=R){
return tree[i];
}
int mid=(tree[i].l+tree[i].r)>>1;
if (L>mid) return Query(L,R,rc);
else if (R<=mid) return Query(L,R,lc);
else return Query(L,R,lc)+Query(L,R,rc);
//这里就不用再重复一遍节点合并的过程
}
};
using namespace SegmentTree;
int main(){
int n=read();
for (register int i=1;i<=n;++i){
a[i]=read();
}
Build(1,n,1);
int q=read();
while (q--){
int L=read(),R=read();
node temp=Query(L,R,1);
printf("%d\n",temp.maxn);
}
}

猫树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define MAXN (1<<20)+7
#define MAXM 23
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^'0');
ch=getchar();
}
return x*f;
}
int cnt[MAXN];
inline void Init(int len){
cnt[1]=0;
for(register int i=2;i<=len<<1;++i){
cnt[i]=cnt[i>>1]+1;
}
}
inline void write(int x){
if(x<0)putchar('-'),x*=-1;
if(!x)putchar(48);
static int sta[45],tp;
for(tp=0;x;x/=10)sta[++tp]=x%10;
for(;tp;putchar(sta[tp--]^48));
putchar('\n');
}
int a[MAXN];
namespace Meow_Tree{
int pos[MAXN];//编号
int maxn[MAXM][MAXN],pmax[MAXM][MAXN];//区间最大值,包含端点最大值
void Build(int l,int r,int i,int d){
if (l==r) {
pos[l]=i;
return ;
}
int mid=(l+r)>>1;
int sum,Max;
sum=pmax[d][mid]=maxn[d][mid]=a[mid],Max=max(a[mid],0);
for (register int i=mid-1;i>=l;--i){
sum+=a[i],Max+=a[i];
pmax[d][i]=max(pmax[d][i+1],sum);
maxn[d][i]=max(maxn[d][i+1],Max);
Max=max(Max,0);
}
sum=pmax[d][mid+1]=maxn[d][mid+1]=a[mid+1],Max=max(a[mid+1],0);
for (register int i=mid+2;i<=r;++i){
sum+=a[i],Max+=a[i];
pmax[d][i]=max(pmax[d][i-1],sum);
maxn[d][i]=max(maxn[d][i-1],Max);
Max=max(Max,0);
}
Build(l,mid,i<<1,d+1);
Build(mid+1,r,i<<1|1,d+1);
}
inline int Query(int l,int r){
if (l==r) return a[l];
int p=cnt[pos[l]]-cnt[pos[l]^pos[r]];
return max(max(maxn[p][l],maxn[p][r]),pmax[p][l]+pmax[p][r]);
}
}
using namespace Meow_Tree;
int main(){
int n=read();
for (register int i=1;i<=n;++i) {
a[i]=read();
}
int len=2;
while (len<n) len<<=1;//要建完全2叉树
Init(len);
Build(1,len,1,1);
int m=read();
while (m--){
int l=read(),r=read();
printf("%d\n",Query(l,r));
}
}

 评论