zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",res);
    			}
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Algorithm --> KMP算法
    Algorithm --> 快速排序
    Algorithm --> 阶乘和因子
    Algorithm --> Dijkstra和Floyd最短路径算法
    Algorithm --> 二分图最大匹配
    Algorithm --> 邮票连续组合问题
    Algorithm --> 字母重排
    Algorithm --> 6174问题
    Algorithm --> 字符串中最长不重合子串长度
    Algorithm --> DFS和BFS
  • 原文地址:https://www.cnblogs.com/lyttt/p/13982610.html
Copyright © 2011-2022 走看看