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

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

题目链接

题目解析

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

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

嘿嘿,\(get\)到了。

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

\(ans=[l1,r1]\)的最大后缀+\(sum[r1+1,l2-1]\)+\([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,l2-1]\),这里可能在查询时出现\(l>r\)的情况,要特判一下。

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

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

如果改成\(ans=[l1,r1-1]\)的最大后缀+\(sum[r1,l2]\)+\([l2+1,r2]\)的最大前缀,那就意味着左右两个区间的端点必须是\(r1-1\)\(l2+1\),但不一定啊,它可以不包括\(r1-1\)\(l2+1\),所以这么写还要判断一下\([l1,r1-1]\)的最大后缀和\([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 @ 2020-11-15 22:03  Starlight_Glimmer  阅读(81)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end