线段树 - Can you answer these queries III - SPOJ - GSS3
给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“1 x y”,查询区间 [x,y] 中的最大连续子段和,即 m a x x ≤ l ≤ r ≤ y ∑ i = l r A [ i ] max_{x≤l≤r≤y}{∑^r_{i=l}A[i]} maxx≤l≤r≤y∑i=lrA[i]。
2、“0 x y”,把 A[x] 改成 y。
对于每个查询指令,输出一个整数表示答案。
输入格式
第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行每行3个整数k,x,y,k=1表示查询(此时如果x>y,请交换x,y),k=0表示修改。
输出格式
对于每个查询指令输出一个整数表示答案。
每个答案占一行。
数据范围
N ≤ 50000 , M ≤ 50000 , ∣ y ∣ ≤ 10000 N≤50000,M≤50000,|y|≤10000 N≤50000,M≤50000,∣y∣≤10000
Example
Input:
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
Output:
6
4
-3
分析:
单 点 修 改 + 区 间 查 询 。 单点修改+区间查询。 单点修改+区间查询。
对 于 最 大 连 续 子 段 和 , 节 点 结 构 体 中 需 要 存 储 每 个 区 间 的 和 t m a x 。 对于最大连续子段和,节点结构体中需要存储每个区间的和tmax。 对于最大连续子段和,节点结构体中需要存储每个区间的和tmax。
记 总 区 间 为 [ L , R ] , 总 区 间 中 点 m i d = L + R 2 , 则 对 于 被 查 询 区 间 [ l , r ] : 记总区间为[L,R],总区间中点mid=\frac{L+R}{2},则对于被查询区间[l,r]: 记总区间为[L,R],总区间中点mid=2L+R,则对于被查询区间[l,r]:
① 、 若 r ≤ m i d , 则 直 接 在 子 区 间 [ L , m i d ] 中 查 询 区 间 [ l , r ] 内 的 最 大 连 续 子 段 和 。 ①、若r≤mid,则直接在子区间[L,mid]中查询区间[l,r]内的最大连续子段和。 ①、若r≤mid,则直接在子区间[L,mid]中查询区间[l,r]内的最大连续子段和。
② 、 若 l > m i d , 则 直 接 在 子 区 间 [ m i d + 1 , R ] 中 查 询 区 间 [ l , r ] 内 的 最 大 连 续 子 段 和 。 ②、若l>mid,则直接在子区间[mid+1,R]中查询区间[l,r]内的最大连续子段和。 ②、若l>mid,则直接在子区间[mid+1,R]中查询区间[l,r]内的最大连续子段和。
③ 、 若 l ≤ m i d ≤ r , 记 区 间 [ l , m i d ] 间 的 最 大 连 续 子 段 和 为 l m a x , 区 间 [ m i d , r ] 间 的 最 大 连 续 子 段 和 为 r m a x , ③、若l≤mid≤r,记区间[l,mid]间的最大连续子段和为lmax,区间[mid,r]间的最大连续子段和为rmax, ③、若l≤mid≤r,记区间[l,mid]间的最大连续子段和为lmax,区间[mid,r]间的最大连续子段和为rmax,
则 区 间 [ l , r ] 内 的 最 大 连 续 子 段 和 为 l m a x + r m a x 。 \qquad则区间[l,r]内的最大连续子段和为lmax+rmax。 则区间[l,r]内的最大连续子段和为lmax+rmax。
因 此 , 节 点 结 构 体 中 还 需 要 存 储 每 个 区 间 的 l m a x 和 r m a x 。 因此,节点结构体中还需要存储每个区间的lmax和rmax。 因此,节点结构体中还需要存储每个区间的lmax和rmax。
l m a x : 左 半 区 间 的 最 大 连 续 后 缀 和 ; r m a x : 右 半 区 间 的 最 大 连 续 前 缀 和 。 lmax:左半区间的最大连续后缀和;rmax:右半区间的最大连续前缀和。 lmax:左半区间的最大连续后缀和;rmax:右半区间的最大连续前缀和。
新 的 问 题 出 现 了 : 如 何 更 新 l m a x 和 r m a x ? 新的问题出现了:如何更新lmax和rmax? 新的问题出现了:如何更新lmax和rmax?
① 、 区 间 [ l , r ] 的 最 大 连 续 前 缀 和 为 子 区 间 [ l , r k ] 的 区 间 和 , ①、区间[l,r]的最大连续前缀和为子区间[l,r_k]的区间和, ①、区间[l,r]的最大连续前缀和为子区间[l,rk]的区间和,
Ⅰ
、
若
r
k
≤
m
i
d
,
则
l
m
a
x
[
l
,
r
k
]
=
l
m
a
x
[
l
,
m
i
d
]
。
如
下
图
:
\qquadⅠ、若r_k≤mid,则lmax_{[l,r_k]}=lmax_{[l,mid]}。如下图:
Ⅰ、若rk≤mid,则lmax[l,rk]=lmax[l,mid]。如下图:
Ⅱ
、
若
r
k
>
m
i
d
,
则
l
m
a
x
[
l
,
r
]
=
s
u
m
[
l
,
m
i
d
]
+
l
m
a
x
[
m
i
d
+
1
,
r
k
]
。
如
下
图
:
\qquad Ⅱ、若r_k>mid,则lmax_{[l,r]}=sum_{[l,mid]}+lmax_{[mid+1,r_k]}。如下图:
Ⅱ、若rk>mid,则lmax[l,r]=sum[l,mid]+lmax[mid+1,rk]。如下图:
② 、 区 间 [ l , r ] 的 最 大 连 续 后 缀 和 同 理 。 ②、区间[l,r]的最大连续后缀和同理。 ②、区间[l,r]的最大连续后缀和同理。
于 是 , 节 点 结 构 体 还 需 存 储 区 间 和 s u m 。 s u m 的 更 新 可 直 接 由 左 、 右 子 区 间 得 到 。 于是,节点结构体还需存储区间和sum。sum的更新可直接由左、右子区间得到。 于是,节点结构体还需存储区间和sum。sum的更新可直接由左、右子区间得到。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
struct node
{
int l,r;
int sum,lmax,rmax,tmax;
}tr[N*4];
int n,m;
int w[N];
void pushup(node &u,node &l,node &r)
{
u.sum=l.sum+r.sum;
u.lmax=max(l.lmax,l.sum+r.lmax);
u.rmax=max(r.rmax,r.sum+l.rmax);
u.tmax=max(max(l.tmax,r.tmax),l.rmax+r.lmax);
}
void pushup(int u)
{
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
if(l==r) tr[u]={r,r,w[r],w[r],w[r],w[r]};
else
{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
node query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u];
else
{
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(u<<1,l,r); //最大区间和在左侧
else if(l>mid) return query(u<<1|1,l,r); //最大区间和在右侧
else //最大区间和跨越两个区间
{
node left=query(u<<1,l,r);
node right=query(u<<1|1,l,r);
node res;
pushup(res,left,right);
return res;
}
}
}
void modify(int u,int x,int v)
{
if(tr[u].l==x&&tr[u].r==x) tr[u]={x,x,v,v,v,v};
else
{
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
scanf("%d",&m);
int op,x,y;
while(m--)
{
scanf("%d%d%d",&op,&x,&y);
if(op==1)
{
if(x>y) swap(x,y);
printf("%d\n",query(1,x,y).tmax);
}
else modify(1,x,y);
}
return 0;
}