求区间最大子段和(线段树)

来源:互联网 发布:淘宝主页全屏轮播 编辑:程序博客网 时间:2024/06/05 00:08

填坑。。。
线段树需要维护的是:
左端点 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[N];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;}
阅读全文
0 0
原创粉丝点击