LA 3938 "Ray, Pass me the dishes!" / 线段树单点更新

中间有一个| 我写成了|| 查了好几个小时 我日

每次询问一段区间最大和的起始和终点坐标 x,y

x y 尽量小

对于某个节点 最大和要么是左子树的最大和 要么是右子树的最大值 要么是左右各有

最后一中情况要保存最大前缀和最大后缀 取左子树的最大后缀和右子树的最大前缀

最大前缀也要更新

一个节点的最大前缀等于1.左子树的最大前缀2.坐子树所有数的和加上右子树的和           2者取大

最大后缀相同

和相同x尽量小x一样y尽量小 多加判断

#include <iostream>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <set>
#include <vector>
#include <cstring>
#include <algorithm>

#define LL long long
using namespace std;
const int maxn = 500010;
struct node
{
	int l;
	int r;
	int sl;
	int sr;
	int ll;
	int rr;
	LL pre;
	LL post;
	LL sum;
}a[maxn<<2];

LL b[maxn];

void build(int l, int r, int rt)
{
	a[rt].l = l;
	a[rt].r = r;
	if(l == r)
	{
		a[rt].ll = a[rt].rr = l;
		a[rt].sl = a[rt].sr = l;
		a[rt].sum = a[rt].pre = a[rt].post = b[l] - b[l-1];
		return;
	}
	int m = (l + r) >> 1;
	build(l, m, rt<<1);
	build(m+1, r, rt<<1|1);
	
	a[rt].pre = a[rt<<1].pre;
	a[rt].ll = a[rt<<1].ll;
	if(a[rt].pre < b[m] - b[l-1] + a[rt<<1|1].pre)
	{
		a[rt].pre = b[m] - b[l-1] + a[rt<<1|1].pre;
		a[rt].ll = a[rt<<1|1].ll;
	}
	
	a[rt].post = a[rt<<1|1].post;
	a[rt].rr = a[rt<<1|1].rr;
	if(a[rt].post <= b[r] - b[m] + a[rt<<1].post)
	{
		a[rt].post = b[r] - b[m] + a[rt<<1].post;
		a[rt].rr = a[rt<<1].rr;
	}
	
	a[rt].sum = a[rt<<1].sum;
	a[rt].sl = a[rt<<1].sl;
	a[rt].sr = a[rt<<1].sr;
	if(a[rt].sum < a[rt<<1|1].sum)
	{
		a[rt].sum = a[rt<<1|1].sum;
		a[rt].sl = a[rt<<1|1].sl;
		a[rt].sr = a[rt<<1|1].sr;
	}
	if(a[rt].sum < a[rt<<1].post + a[rt<<1|1].pre)
	{
		a[rt].sum = a[rt<<1].post + a[rt<<1|1].pre;
		a[rt].sl = a[rt<<1].rr;
		a[rt].sr = a[rt<<1|1].ll;
	}
	else if(a[rt].sum == a[rt<<1].post + a[rt<<1|1].pre)
	{
		if(a[rt].sl > a[rt<<1].rr || (a[rt].sl == a[rt<<1].rr && a[rt].sr > a[rt<<1|1].ll))
		{
			a[rt].sl = a[rt<<1].rr;
			a[rt].sr = a[rt<<1|1].ll;
		}
	}
}
node query(int l, int r, int rt)
{
	if(l == a[rt].l && r == a[rt].r)
		return a[rt];
	int m = (a[rt].l + a[rt].r) >> 1;
	if(l > m)
		return query(l, r, rt<<1|1);
	else if(r <= m)
		return query(l, r, rt<<1);
	else	
	{	
		node left = query(l, m, rt<<1);
		node right = query(m+1, r, rt<<1|1);
		node u = left;
		if(u.sum < right.sum)
			u = right;
		if(u.sum < left.post + right.pre)
		{
			u.sum = left.post + right.pre;
			u.sl = left.rr;
			u.sr = right.ll;
		}
		else if(u.sum == left.post + right.pre)
		{
			if(u.sl > left.rr || (u.sl == left.rr && u.sr > right.ll))
			{
				u.sl = left.rr;
				u.sr = right.ll;
			}
		}
		
		u.ll = left.ll;
		u.pre = left.pre;
		if(u.pre < b[m] - b[l-1] + right.pre)
		{
			u.pre = b[m] - b[l-1] + right.pre;	
			u.ll = right.ll;
		}
		
		u.rr = right.rr;
		u.post = right.post;
		if(u.post <= b[r] - b[m] + left.post)
		{
			u.post = b[r] - b[m] + left.post;
			u.rr = left.rr;
		}
		return u;
	}
}
int main()
{
	int cas = 1;
	int n, m;
	while(scanf("%d %d", &n, &m) != EOF)
	{
		b[0] = 0;
		for(int i = 1; i <= n; i++)
		{
			scanf("%lld", &b[i]);
			b[i] += b[i-1];
		}
		build(1, n, 1);
		printf("Case %d:\n", cas++);
		while(m--)
		{
			int x, y;
			scanf("%d %d", &x, &y);
			node ret = query(x, y, 1);
			printf("%d %d\n",ret.sl,ret.sr);
		}
	}
	return 0;
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值