线段树维护连续区间最大子段和

一、线段树维护连续区间最大子段和之单点更新   

             

Snowy Smile

题意:

给你n个点(小于等于2e3个),每个点有个价值val,有个x,y坐标(这三个值都是1e9的)

让你求一个最大的子矩阵和

题解:

线段树的精髓——区间合并

因为x,y太大,所以先考虑离散化,之后是一个n*m的矩形

枚举上,下边界,线段树维护区间最大子段和

 

1、合并的答案要么左区间的子段和,要么是右区间的子段和,要么是中间的一部分

2、考虑中间这部分怎么求,也就是说,其实求一个区间的最大,前后缀就行了,因为中间这部分就是

左区间的最大后缀+右区间的最大前缀

3、考虑怎么维护区间的前后缀、前缀的话,肯定是左区间前缀   和  左区间的区间和加右区间的前缀  取一个最大值

后缀同理

 

详细区间线段树维护 看博客

为了简单起便,只需要查询的是整个区间[1,n]所有,连询问函数也不用写了,直接找根节点的值就行了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4+7;
int T,n;
struct node
{
    ll lsum,rsum,sum,Ans;
    void update(ll x)
    {
        lsum += x;
        rsum += x;
        sum += x;
        Ans += x;
    }
} tree[maxn<<2];
void push_up(int x)
{
    tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
    tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
    tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
    tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int L = 1,int R = n,int x = 1)
{
    tree[x].lsum = tree[x].rsum = 0;
    tree[x].sum = tree[x].Ans = 0;
    if(L == R)return;
    int mid = L+R>>1;
    build(L,mid,x<<1),build(mid+1,R,x<<1|1);
    //push_up(x);
}
void update(int k,ll val,int L=1,int R=n,int x = 1)
{
    if(L == R)
    {
        tree[x].update(val);
    }
    else
    {
        int mid = L+R>>1;
        if(k<=mid)update(k,val,L,mid,x<<1);
        else update(k,val,mid+1,R,x<<1|1);
        push_up(x);
    }
}

vector<int> vecL,vecU;
vector<pair<int,ll> > vec[maxn];
ll x[maxn],y[maxn],val[maxn];
int main()
{
    for(scanf("%d",&T); T--;)
    {
        vecL.clear();
        vecU.clear();
        for(int i=1; i<=n; i++)vec[i].clear();
        
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lld%lld%lld",&x[i],&y[i],&val[i]);
            vecL.push_back(x[i]);
            vecU.push_back(y[i]);
        }
        sort(vecL.begin(),vecL.end());
        vecL.erase(unique(vecL.begin(),vecL.end()),vecL.end());
        sort(vecU.begin(),vecU.end());
        vecU.erase(unique(vecU.begin(),vecU.end()),vecU.end());
        for(int i=1; i<=n; i++)
        {
            int x_ = lower_bound(vecL.begin(),vecL.end(),x[i])-vecL.begin()+1;//+1表示从1-n
            int y_ = lower_bound(vecU.begin(),vecU.end(),y[i])-vecU.begin()+1;
            vec[x_].push_back(make_pair(y_,val[i]));
        }
        ll ans = 0;
        for(int i=1; i<=n; i++)
        {
            build();
            for(int j=i; j<=n; j++)
            {
                for(auto k:vec[j])update(k.first,k.second);
                ans = max(ans,tree[1].Ans);//直接找根节点
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

二、线段树维护连续区间最大子段和之区间查询       

 

                

GSS1 - Can you answer these queries I

题意:

给定一序列,每次给定区间l,y查询该区间最大连续子段和

题解:

还是线段树维护连续区间最大子段和,这次是区间查询

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4+7;
int T,n,m;
int a[maxn];
struct node
{
    ll lsum,rsum,sum,Ans;
    void update(ll x)
    {
        lsum += x;
        rsum += x;
        sum += x;
        Ans += x;
    }
} tree[maxn<<2];
void push_up(int x)
{
    tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
    tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
    tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
    tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int L = 1,int R = n,int x = 1)
{
    if(L == R)
    {
        tree[x].lsum = tree[x].rsum = a[L];
        tree[x].sum = tree[x].Ans = a[L];
        return;
    }
    int mid = L+R>>1;
    build(L,mid,x<<1),build(mid+1,R,x<<1|1);
    push_up(x);
}
node query(int l,int r,int L = 1,int R = n,int x = 1)
{
    if (l <= L && R <= r)return tree[x];
    int mid=(L+R)>>1;
    if (r <= mid) return query(l, r, L, mid, x<<1);
    if (mid < l) return query(l, r, mid+1 , R, x<<1|1);
    node lo = query(l,r,L,mid,x<<1), ro = query(l,r,mid+1,R,x<<1|1),ans;
    ans.sum=lo.sum+ro.sum;
    ans.lsum=max(lo.lsum,lo.sum+ro.lsum);
    ans.rsum=max(ro.rsum,ro.sum+lo.rsum);
    ans.Ans=max(max(lo.Ans,ro.Ans),lo.rsum+ro.lsum);
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)scanf("%d",&a[i]);
    build();
    scanf("%d",&m);
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n", query(x, y).Ans);
    }
    return 0;
}

三、线段树维护连续区间最大子段和之单点修改+区间查询        

 

                   

你能回答这些问题吗

题意:

给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

1、“1 x y”,查询区间 [x,y] 中的最大连续子段和,即 max_{x\leq l\leq r\leq y }\{\sum_{i=l}^{r}A[i] \}

2、“2 x y”,把 A[x] 改成 y

对于每个查询指令,输出一个整数表示答案。

题解:

还是线段树维护

这次是上面两个结合版本

对于区间查询需要注意

因为并不能直接区间做加法,返回的结构体嘛

对于查询区间全部包含当前区间时,直接返回结构体即可

否则,在右边往右查,在左边往左查

剩下的就是跨越左右子区间了,那么,返回 合并的左右查询的结构体 就行了

模板一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
int T,n,m;
ll a[maxn];
struct node
{
    ll lsum,rsum,sum,Ans;
    void update(ll x)
    {
        lsum = x;
        rsum = x;
        sum = x;
        Ans = x;
    }
} tree[maxn<<2];
void push_up(int x)
{
    tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
    tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
    tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
    tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int L = 1,int R = n,int x = 1)
{
    if(L == R)
    {
        tree[x].lsum = tree[x].rsum = a[L] = tree[x].sum = tree[x].Ans = a[L];
        return;
    }
    int mid = L+R>>1;
    build(L,mid,x<<1),build(mid+1,R,x<<1|1);
    push_up(x);
}
void update(int k,ll val,int L=1,int R=n,int x = 1)
{
    if(L == R)
    {
        tree[x].update(val);
    }
    else
    {
        int mid = L+R>>1;
        if(k<=mid)update(k,val,L,mid,x<<1);
        else update(k,val,mid+1,R,x<<1|1);
        push_up(x);
    }
}

node query(int l,int r,int L = 1,int R = n,int x = 1)
{
    if (l <= L && R <= r)return tree[x];
    int mid=(L+R)>>1;
    if (r <= mid) return query(l, r, L, mid, x<<1);
    if (mid < l) return query(l, r, mid+1 , R, x<<1|1);
    node lo = query(l,r,L,mid,x<<1), ro = query(l,r,mid+1,R,x<<1|1),ans;
    ans.sum=lo.sum+ro.sum;
    ans.lsum=max(lo.lsum,lo.sum+ro.lsum);
    ans.rsum=max(ro.rsum,ro.sum+lo.rsum);
    ans.Ans=max(max(lo.Ans,ro.Ans),lo.rsum+ro.lsum);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)scanf("%lld",&a[i]);
    build();
    while(m--)
    {
        int k,x,y;
        scanf("%d%d%d",&k,&x,&y);
        if(k==1)
        {
            if(x>y)swap(x,y);
            printf("%lld\n", query(x,y).Ans);
        }
        else update(x,y);
    }
    return 0;
}

模板二

/*
线段树维护区间最大子段和
https://www.acwing.com/problem/content/description/246/
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
int T,n,m;
ll a[maxn];
struct node
{
    ll lsum,rsum,sum,Ans;
    int l,r;
    void update(ll x)
    {
        lsum = x;
        rsum = x;
        sum = x;
        Ans = x;
    }
} tree[maxn<<2];
void push_up(int x)
{
    tree[x].sum = tree[x<<1].sum+tree[x<<1|1].sum;
    tree[x].lsum = max(tree[x<<1].lsum,tree[x<<1].sum+tree[x<<1|1].lsum);
    tree[x].rsum = max(tree[x<<1|1].rsum,tree[x<<1|1].sum+tree[x<<1].rsum);
    tree[x].Ans = max(max(tree[x<<1].Ans,tree[x<<1|1].Ans),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int l,int r,int x)
{
    tree[x].l=l;
    tree[x].r=r;
    if(l == r)
    {
        tree[x].lsum = tree[x].rsum = tree[x].sum = tree[x].Ans = a[l];
        return;
    }
    int mid = l+r>>1;
    build(l,mid,x<<1),build(mid+1,r,x<<1|1);
    push_up(x);
}
void update(int x,int k,ll val)
{
    int l=tree[x].l;
    int r=tree[x].r;
    if(l == r)
    {
        tree[x].update(val);
        return ;
    }
    else
    {
        int mid = l+r>>1;
        if(k<=mid)update(x<<1,k,val);
        else update(x<<1|1,k,val);
        push_up(x);
    }
}
node query(int x,int l,int r)
{
    int L=tree[x].l;
    int R=tree[x].r;
    if(l<=L && R<=r)return tree[x];
    int mid=(L+R)>>1;
    if (r<=mid) return query(x<<1,l,r);
    if (l>mid) return query(x<<1|1,l,r);
    node lo = query(x<<1,l,r), ro = query(x<<1|1,l,r),ans;
    ans.sum=lo.sum+ro.sum;
    ans.lsum=max(lo.lsum,lo.sum+ro.lsum);
    ans.rsum=max(ro.rsum,ro.sum+lo.rsum);
    ans.Ans=max(max(lo.Ans,ro.Ans),lo.rsum+ro.lsum);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)scanf("%lld",&a[i]);
    build(1,n,1);
    while(m--)
    {
        int k,x,y;
        scanf("%d%d%d",&k,&x,&y);
        if(k==1)
        {
            if(x>y)swap(x,y);
            printf("%lld\n", query(1,x,y).Ans);
        }
        else update(1,x,y);
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值