求区间最大子段和(线段树)
2017-06-17 20:39
330 查看
填坑。。。
线段树需要维护的是:
左端点 x
右端点 y (本人喜欢直接维护端点)
[x,y]内的最大子段和 ms
[x,y]的区间和 s
[x,y]内的紧靠左端点的最大子段和 ls
[x,y]内的紧靠右端点的最大子段和 rs
困难就是,update和ask(l,r)询问[l,r]区间内的最大子段和
那我们一步一步来
s的维护很常规,
ls:有两种情况:
1.该区间内的ls是ta左儿子的ls
2.该区间内的ls是左儿子的s+右儿子的ls
同理,rs:有两种情况:
1.该区间内的rs是ta右儿子的rs
2.该区间内的rs是右儿子的s+左儿子的rs
而ms有三种情况:
1.该区间内的ms是左儿子的ms
2.该区间内的ms是右儿子的ms
3.该区间内的ms是左儿子的rs+右儿子的ls
解决一个,那询问呢~
其实道理是一样的
[l,r]内的区间最大子段和分以下几种情况:
1.独立的存在于左儿子或右儿子中
2.左儿子的rs+右儿子的ls
然而如果[l,r]在线段树中是一个节点(我们单独维护过),那我们直接return ms 就好啦
代码略长
完整代码:
线段树需要维护的是:
左端点 x
右端点 y (本人喜欢直接维护端点)
[x,y]内的最大子段和 ms
[x,y]的区间和 s
[x,y]内的紧靠左端点的最大子段和 ls
[x,y]内的紧靠右端点的最大子段和 rs
困难就是,update和ask(l,r)询问[l,r]区间内的最大子段和
那我们一步一步来
s的维护很常规,
ls:有两种情况:
1.该区间内的ls是ta左儿子的ls
2.该区间内的ls是左儿子的s+右儿子的ls
同理,rs:有两种情况:
1.该区间内的rs是ta右儿子的rs
2.该区间内的rs是右儿子的s+左儿子的rs
而ms有三种情况:
1.该区间内的ms是左儿子的ms
2.该区间内的ms是右儿子的ms
3.该区间内的ms是左儿子的rs+右儿子的ls
void update(int bh) { tree[bh].ms=max(tree[bh<<1].ms,tree[(bh<<1)+1].ms); tree[bh].ms=max(tree[bh].ms,tree[bh<<1].rs+tree[(bh<<1)+1].ls); tree[bh].ls=max(tree[bh<<1].ls,tree[bh<<1].s+tree[(bh<<1)+1].ls); tree[bh].rs=max(tree[(bh<<1)+1].rs,tree[(bh<<1)+1].s+tree[bh<<1].rs); tree[bh].s=tree[bh<<1].s+tree[(bh<<1)+1].s; return; }
解决一个,那询问呢~
其实道理是一样的
[l,r]内的区间最大子段和分以下几种情况:
1.独立的存在于左儿子或右儿子中
2.左儿子的rs+右儿子的ls
然而如果[l,r]在线段树中是一个节点(我们单独维护过),那我们直接return ms 就好啦
代码略长
int askl(int bh,int l,int r) //在[l,r]中查找紧靠左端的最大子段和 { if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ls; int mid=(tree[bh].x+tree[bh].y)>>1; if (r<=mid) askl(bh<<1,l,r); else if (l>mid) askl((bh<<1)+1,l,r); int lans=(bh<<1,l,mid); //左儿子中紧靠左的最大子段和 int rans=((bh<<1)+1,mid+1,r); //右儿子中紧靠左的最大子段和 return max(lans,rans+tree[bh<<1].s); //rans+tree[bh<<1].s } int askr(int bh,int l,int r) //在[l,r]中查找紧靠右端的最大子段和 { if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].rs; int mid=(tree[bh].x+tree[bh].y)>>1; if (r<=mid) askr(bh<<1,l,r); else if (l>mid) askr((bh<<1)+1,l,r); int lans=askr(bh<<1,l,mid); //左儿子中紧靠右的最大子段和 int rans=askr((bh<<1)+1,mid+1,r); //右儿子中紧靠右的最大子段和 return max(rans,lans+tree[(bh<<1)+1].s); } int ask(int bh,int l,int r) //ask是专用来考虑[l,r]的最大子段和独立的存在于左子段中, { //或右子段中,没有跨段的情况 if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ms; int mid=(tree[bh].x+tree[bh].y)>>1; //// if (r<=mid) ask(bh<<1,l,r); else if (l>mid) ask((bh<<1)+1,l,r); int lans=ask(bh<<1,l,mid); int rans=ask((bh<<1)+1,mid+1,r); int ans=max(lans,rans); return max(ans,askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r)); } //askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r) 这是将[l,r]劈成两段,最大子段在两部分中都有一部分 //需要注意的是,在分成两段分别求解时,[l,r]的范围会改变
完整代码:
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=100001;
int n;
int l,r;
int a
;
struct node{
int x,y,ls,rs,s,ms; //ls 紧靠左边的最大子段和 rs 紧靠右边的最大子段和
};
node tree[N<<2];
void update(int bh) { tree[bh].ms=max(tree[bh<<1].ms,tree[(bh<<1)+1].ms); tree[bh].ms=max(tree[bh].ms,tree[bh<<1].rs+tree[(bh<<1)+1].ls); tree[bh].ls=max(tree[bh<<1].ls,tree[bh<<1].s+tree[(bh<<1)+1].ls); tree[bh].rs=max(tree[(bh<<1)+1].rs,tree[(bh<<1)+1].s+tree[bh<<1].rs); tree[bh].s=tree[bh<<1].s+tree[(bh<<1)+1].s; return; }
void build(int bh,int l,int r)
{
tree[bh].x=l; tree[bh].y=r;
if (l==r)
{
tree[bh].s=tree[bh].ms=tree[bh].ls=tree[bh].rs=a[l];
return;
}
int mid=(l+r)>>1;
build(bh<<1,l,mid);
build((bh<<1)+1,mid+1,r);
update(bh);
}
void change(int bh,int mb,int z) //单点修改
{
if (tree[bh].x==tree[bh].y&&tree[bh].x==mb)
{
tree[bh].s=tree[bh].ms=tree[bh].ls=tree[bh].rs=z;
return;
}
int mid=(tree[bh].x+tree[bh].y)>>1;
if (mb<=mid) change(bh<<1,mb,z);
else change((bh<<1)+1,mb,z);
update(bh);
}
int askl(int bh,int l,int r) //在[l,r]中查找紧靠左端的最大子段和
{
if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ls;
int mid=(tree[bh].x+tree[bh].y)>>1;
if (r<=mid) askl(bh<<1,l,r);
else if (l>mid) askl((bh<<1)+1,l,r);
int lans=(bh<<1,l,mid); //左儿子中紧靠左的最大子段和
int rans=((bh<<1)+1,mid+1,r); //右儿子中紧靠左的最大子段和
return max(lans,rans+tree[bh<<1].s); //rans+tree[bh<<1].s
}
int askr(int bh,int l,int r) //在[l,r]中查找紧靠右端的最大子段和
{
if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].rs;
int mid=(tree[bh].x+tree[bh].y)>>1;
if (r<=mid) askr(bh<<1,l,r);
else if (l>mid) askr((bh<<1)+1,l,r);
int lans=askr(bh<<1,l,mid); //左儿子中紧靠右的最大子段和
int rans=askr((bh<<1)+1,mid+1,r); //右儿子中紧靠右的最大子段和
return max(rans,lans+tree[(bh<<1)+1].s);
}
int ask(int bh,int l,int r) //ask是专用来考虑[l,r]的最大子段和独立的存在于左子段中,
{ //或右子段中,没有跨段的情况
if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ms;
int mid=(tree[bh].x+tree[bh].y)>>1; ////
if (r<=mid) ask(bh<<1,l,r);
else if (l>mid) ask((bh<<1)+1,l,r);
int lans=ask(bh<<1,l,mid);
int rans=ask((bh<<1)+1,mid+1,r);
int ans=max(lans,rans);
return max(ans,askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r));
} //askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r) 这是将[l,r]劈成两段,最大子段在两部分中都有一部分
//需要注意的是,在分成两段分别求解时,[l,r]的范围会改变
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
int T;
scanf("%d",&T);
while (T--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d",ask(1,x,y));
}
return 0;
}
相关文章推荐
- gym101138J(树链剖分,线段树维护区间连续子段最大和,好题)
- Spoj 1557 Can you answer these queries II 线段树 任意区间最大子段和 不重复数字
- Spoj 1716 Can you answer these queries III 线段树 单点修改 区间求最大子段和
- Gym101138J ————Valentina and the Gift Tree (树链剖分,区间最大子段和,线段树)
- Spoj 1557 Can you answer these queries II 线段树 随意区间最大子段和 不反复数字
- Spoj 2916 Can you answer these queries V 线段树 求任意重叠区间的最大子段和
- Hust oj 1189 区间最大值 II(线段树RMQ)
- 【算法系列学习】线段树 单点覆盖,区间查询最大值 [kuangbin带你飞]专题七 线段树 B - I Hate It
- HDUOJ---1754 I Hate It (线段树之单点更新查区间最大值)
- 线段树初见——区间询问与改变最大值
- HDU 1754 B I Hate It 线段树 单点更新 区间最大值 模板
- zoj 3633 线段树单点更新 区间最大值
- 线段树部分总结 (单点,区间)更新,区间求和,求最大值(敌兵布阵&I Hate It&A Simple Problem with Integers)
- HDU 2795 Billboard (线段树:区间查询最大值)
- hdu 1754 线段树区间最大值 单点更新
- POJ 题目2892 Tunnel Warfare(线段树单点更新查询,求单点所在最大连续区间长度)
- 区间更新 区间和查询 带有延迟标记 线段树 hdu1698; 附:csa 区间加值,维护最大值
- hdu 1754 I Hate It (线段树--求区间最大值)(基础)
- Hdu 2795 线段树 区间最大值的位置.cpp
- I Hate It HDU - 1754(线段树单点修改,区间求最大值模板)