Jeanny
寂兮,寥兮,独立不改,周行而不殆

我们知道,求一段序列的最大子段和是O(n)的,但是这样是显然会超时的。

我们需要一个数据结构来支持修改和计算的操作,对于这种修改一个而查询区间的问题,考虑使用线段树。

在线段树中,除了左端点,右端点,左儿子指针,右儿子指针之外,新开4个域——max,maxl,maxr,sum,其中sum为该区间的和,max为该区间上的最大子段和,maxl为必须包含左端点的最大子段和,maxr为必须包含右端点的最大子段和。

可以用线段树来统计了注意求得的最大子段和中至少包含1个元素,所以出现了样例那样的输出负值。

修改时:

1、若左儿子的maxr和右儿子的maxl都为负,就从中取较大的为该节点的max(防止一个都不取),反之取二者中正的(都正就都取)。

2、将该节点的max用左右儿子的max更新。

3、该节点的maxl为左儿子的maxl与左儿子sum和右儿子maxl和的最大值。

4、该节点的maxr为右儿子的maxr与右儿子sum和左儿子maxr和的最大值。

5、该节点的sum为左右儿子的sum和。

查询时:

1、如果查询区间覆盖这一节点,将该节点信息返回。

2、如果只与一个儿子有交集,就返回在那个儿子中查找到的信息。

3、如果与两个儿子都有交集,就先分别计算出两个儿子的信息,然后按修改的方式将两个信息合并,然后返回。

4、最后返回的max值即为答案。

#include<cstdio>
#include<iostream>
using namespace std;
struct node{
    int l,r,mm,lm,rm,v;
}tr[2000010];
void update(int k)
{
    tr[k].v=tr[k*2].v+tr[k*2+1].v;
    tr[k].lm=max(tr[k*2].lm,tr[k*2].v+tr[k*2+1].lm);
    tr[k].rm=max(tr[k*2+1].rm,tr[k*2+1].v+tr[k*2].rm);
    tr[k].mm=max(tr[k*2].mm,max(tr[k*2+1].mm,tr[2*k].rm+tr[k*2+1].lm));
}
void build(int l,int r,int k)
{
    tr[k].l=l;
    tr[k].r=r;
    if(l==r)
    {
        cin>>tr[k].v;
        tr[k].lm=tr[k].v;
        tr[k].rm=tr[k].v;
        tr[k].mm=tr[k].v;
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);
    update(k);//回溯时求该区间最大值。
}
void change(int pos,int num,int k){
    if(tr[k].l==pos && tr[k].r==pos){
        tr[k].v=num;
        tr[k].lm=num,tr[k].rm=num,tr[k].mm=num;
        return;
    }
    int mid=(tr[k].l+tr[k].r)>>1;
    if(pos<=mid)
        change(pos,num,k*2);
    else
        change(pos,num,k*2+1);//向下二分搜索 ;
    update(k);//回溯时改变区间最大值;
}
node sear(int l,int r,int k)//由于和update有一样操作直接找结构体
{
    node left1,right1;
    int f1=0,f2=0;
    if(tr[k].l>=l && tr[k].r<=r)
        return tr[k];
    int mid=(tr[k].l+tr[k].r)/2;
    if(l<=mid)
        left1 = sear(l,r,k*2),f1=1;//如果区间包含中线左边 。
    if(r>mid) 
        right1 = sear(l,r,k*2+1),f2=1;//如果区间包含中线右边。
    if(f2 && f1)//如果区间同时包含中线左右边则需进行update时一样的操作。
    {
        node m;
        m.l = left1.l, m.r = right1.r;
        m.v = left1.v+right1.v;
        m.lm = max(left1.lm, left1.v+right1.lm);
        m.rm = max(right1.rm, right1.v+left1.rm);
        m.mm = max(left1.mm, max(right1.mm, left1.rm + right1.lm)) ;
        return m;
    }
    if(f1)//只包含左边;
        return left1;
    if(f2)//只包含右边;
        return right1;
}


int main()
{
    int n,m,type,le,ri;
    cin>>n>>m;
    build(1,n,1);
    for(int i=1;i<=m;i++)
    {
        cin>>type>>le>>ri;
        if(type==1 && le>ri)
            swap(le,ri);
        if(type==1)
        {
            node ans=sear(le,ri,1);
            cout<<ans.mm<<endl;
        }
        else
            change(le,ri,1);
    }

    return 0;
}

 

posted on 2020-06-28 16:33  Jeanny  阅读(116)  评论(0)    收藏  举报