P4513 小白逛公园 & SP1716 GSS3 - Can you answer these queries III(线段树维护区间最大字段和)

[题目描述1] 题目描述2

这两道题几乎一模一样,都是线段树实现单点修改,查询区间最大字段和的功能

这类题目的难点通常在于如何 p u s h u p pushup pushup p u s h d o w n pushdown pushdown(这题因为是单点修改没有 p u s h d o w n pushdown pushdown)

先说一句废话一个区间的最大字段和有三种情况: 它左区间的最大字段和,它右区间的最大字段和,它左区间取一部分和它右区间取一部分

前两种情况pushup都非常好实现,现在只剩第三种了。我们可以对于线段树的一个节点可以维护以下的值,这样就能实现pushuup了:

sum:该区间的值
lsum:以该区间左端点为起点的最大字段和
rsum:以该区间右端点为终点的最大字段和
max_sum:该区间的最大字段和

不难发现,上述的第三种情况就变成了:左区间的 r s u m rsum rsum + 右区间的 l s u m lsum lsum
这样一来我们就可以写下本题的pushup了:

当前区间的 s u m sum sum = 左区间的 s u m sum sum + 右区间的 s u m sum sum(这个不用解释吧)
当前区间的 l s u m lsum lsum = max(左区间的 l s u m lsum lsum,左区间的 s u m sum sum + 右区间的 l s u m lsum lsum)

(自己画画图就明白了)

当前区间的 r s u m rsum rsum = max(右区间的 r s u m rsum rsum,右区间的 s u m sum sum + 左区间的 s u m sum sum)

(与上面同理)

当前区间的 m a x max max s u m sum sum = max(左区间的 m a x max max s u m sum sum,右区间的 m a x max max_ s u m sum sum,左区间的 r s u m rsum rsum + 右区间的 l s u m lsum lsum)

有了这个我们的程序就大功告成了

附上小白逛公园的代码(另一道题几乎一模一样,主程序部分稍加调整就行了)

# include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;
const int inf = INT_MAX;

#define ll long long
#define FOR(i,a,b) for(int i = a;i <= b;i++)
#define _FOR(i,a,b) for(int i = a;i >= b;i--)
#define l(x) x << 1
#define r(x) x << 1 | 1

template<typename T> void read(T &x)
{
    x = 0;int f = 1;
    char c = getchar();
    for(;!isdigit(c);c = getchar()) if(c == '-') f = -1;
    for(;isdigit(c);c = getchar()) x = x * 10 + c - '0';
    x *= f;
}

struct node
{
    int l,r;
    int sum,lsum,rsum,max_sum;
} t[N * 4];

int n,m;
int a[N];

void pushup(int p)
{
	t[p].sum = t[l(p)].sum + t[r(p)].sum;
	t[p].lsum = max(t[l(p)].lsum,t[l(p)].sum + t[r(p)].lsum);
	t[p].rsum = max(t[r(p)].sum + t[l(p)].rsum,t[r(p)].rsum);
	t[p].max_sum = max(max(t[l(p)].max_sum,t[r(p)].max_sum),t[l(p)].rsum + t[r(p)].lsum);
}

void build(int p,int l,int r)
{
    t[p].l = l,t[p].r = r;
    if(l == r) 
	{
		t[p].lsum = t[p].rsum = t[p].max_sum = t[p].sum = a[l];
		return ;
	}
    int mid = (l + r) >> 1;
    build(l(p),l,mid);
    build(r(p),mid + 1,r);
    pushup(p);
}

void change(int p,int x,int y)
{
    if(t[p].l == t[p].r) 
	{
		t[p].lsum = t[p].rsum = t[p].max_sum = t[p].sum = y;
		return ;
	}
    int mid = (t[p].l + t[p].r) >> 1;
    if(x <= mid) change(l(p),x,y);
    else change(r(p),x,y);
    pushup(p);
}

node ask(int p,int l,int r)
{
    if(l <= t[p].l && r >= t[p].r) return t[p];
    int mid = (t[p].l + t[p].r) >> 1;
    if(l <= mid && r <= mid) return ask(l(p),l,r);
    else if(l > mid && r > mid) return ask(r(p),l,r);
    else
    {
    	node ans;
    	node x = ask(l(p),l,r),y = ask(r(p),l,r);
    	ans.sum = x.sum + y.sum;
		ans.lsum = max(x.lsum,x.sum + y.lsum);
		ans.rsum = max(y.sum + x.rsum,y.rsum);
		ans.max_sum = max(max(x.max_sum,y.max_sum),x.rsum + y.lsum);
		return ans;
	}
}

int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    read(n),read(m);
    FOR(i,1,n)
    	read(a[i]);
    build(1,1,n);
    FOR(i,1,m)
    {
  		int x,y,z;
  		read(x),read(y),read(z);
  		if(x == 2)
  	  		change(1,y,z);
  		else
  	  	{
  	  		if(y > z) swap(y,z);
			printf("%d\n",ask(1,y,z).max_sum);
		}
  }
  return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值