把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

SPOJ2916 GSS5-Can you answer these queries V【线段树】

题目链接

题目解析

我们发现译者在题面里加了一句非常有趣的话:

但是不保证端点所在的区间不重合

嘿嘿,get到了。

我们进行分类讨论,如果r1<l2,那么事情就变得简单起来了:

ans=[l1,r1]的最大后缀+sum[r1+1,l21]+[l2,r2]的最大前缀

然后那玩意儿用GSS1的方法维护就好了

而如果r1>l2,也就是两个区间有交集,那么还会有更多种情况:
第一种:

ans=[l2,r1]的最大子段和

第二种:

左端点在[l1,l2],右端点在[l2,r2]

ans=[l1,l2]最大后缀+[l2,r2]最大前缀

第三种:

左端点在[l1,r1],右端点在[r1,r2]

ans=[l1,r1]最大后缀+[r1,r2]最大前缀

以上三种情况都可以用GSS1的方法维护,三个方案取max即可。


但是,

这道题真没想的那么简单,

主要是它有很多边界上的细节容易出错,而且不好改。

首先,在r1<l2的情况里,有一个sum[r1+1,l21],这里可能在查询时出现l>r的情况,要特判一下。

其次,我们前面没有讨论r1==l2的情况。

我以为随便丢在哪种情况里都可以来着,但是如果丢在第一种情况,边界就会被算两次,所以最好丢到第二种情况去。

如果改成ans=[l1,r11]的最大后缀+sum[r1,l2]+[l2+1,r2]的最大前缀,那就意味着左右两个区间的端点必须是r11l2+1,但不一定啊,它可以不包括r11l2+1,所以这么写还要判断一下[l1,r11]的最大后缀和[l2+1,r2]的最大前缀是不是负数,如果是负数就不加(好麻烦

(具体写法

再然后,就是第二个大类里面的,我以为下面的边界可以归在左右随便哪个区间里就可以了,毕竟边界点总会在答案里。

但没有想到的是,这道题它不能随便,虽然边界点总会在答案里,但是不同的写法对边界点左右两边是否一定要在区间里产生影响(类似于刚才的那个讨论)。这么说可能有点抽象,我具体写在代码注释里了。


►Code View

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define N 10005
#define DEL 100000
#define INF 0x3f3f3f3f
#define MOD 998244353
LL rd()
{
	LL x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
	return f*x;
}
int n;
struct node{
	int sum,mx,mxl,mxr;
}tree[N<<2];
void PushUp(int i)
{
	tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
	tree[i].mx=max(max(tree[i<<1].mx,tree[i<<1|1].mx),tree[i<<1].mxr+tree[i<<1|1].mxl);
	tree[i].mxl=max(tree[i<<1].mxl,tree[i<<1].sum+tree[i<<1|1].mxl);
	tree[i].mxr=max(tree[i<<1|1].mxr,tree[i<<1|1].sum+tree[i<<1].mxr);
}
void Build(int i,int l,int r)
{
	if(l==r)
	{
		tree[i].sum=tree[i].mx=tree[i].mxl=tree[i].mxr=rd();
		return ;
	}
	int mid=(l+r)>>1;
	Build(i<<1,l,mid);
	Build(i<<1|1,mid+1,r);
	PushUp(i);
}
node Query(int i,int l,int r,int ql,int qr)
{
	if(ql>qr)
	{//边界+1-1之后可能会出现这种情况 需要特判 
		node res;
		res.sum=res.mx=res.mxl=res.mxr=0;
		return res;
	}
	if(ql<=l&&r<=qr) return tree[i];
	int mid=(l+r)>>1;
	if(qr<=mid) return Query(i<<1,l,mid,ql,qr);
	else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr);
	else
	{
		node x=Query(i<<1,l,mid,ql,qr),y=Query(i<<1|1,mid+1,r,ql,qr),res;
		res.sum=x.sum+y.sum;
		res.mx=max(max(x.mx,y.mx),x.mxr+y.mxl);
		res.mxl=max(x.mxl,x.sum+y.mxl);
		res.mxr=max(y.mxr,y.sum+x.mxr);
		return res;
	}
}
int main()
{
	int T=rd();
	while(T--)
	{
		n=rd();
		Build(1,1,n);
		int Q=rd();
		while(Q--)
		{
			int l1=rd(),r1=rd(),l2=rd(),r2=rd();
			if(r1<l2)
			{//这里不能取等 不然边界会被算2次 
				node x=Query(1,1,n,l1,r1),y=Query(1,1,n,l2,r2),z=Query(1,1,n,r1+1,l2-1)/*注意边界+1-1*/;
				printf("%d\n",x.mxr+z.sum+y.mxl);
			}
			else
			{
				int res=Query(1,1,n,l2,r1).mx;
				node x=Query(1,1,n,l1,l2-1),y=Query(1,1,n,l2,r2);
				//啊 这里之前写的 x=Query(1,1,n,l1,l2),y=Query(1,1,n,l2+1,r2) 就过不了
				//那样写的话 右端点恰好在l2上的情况就无法被计算到
				//而左端点恰好在l2上的情况可以在第一种和第三种情况中被计算 
				//同理 下面也必须写成r1和r1+1 否则左端点恰好在r1的情况就无法被计算到 
				res=max(res,x.mxr+y.mxl);
				x=Query(1,1,n,l1,r1),y=Query(1,1,n,r1+1,r2);
				res=max(res,x.mxr+y.mxl);
				printf("%d\n",res);
			}
		}
	}
	return 0;
}

posted @   Starlight_Glimmer  阅读(81)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2019-11-15 CSP考试策略
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示