GSS1 - Can you answer these queries I
Descripition
You are given a sequence A[1], A[2], …, A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A query is defined as follows:
Query(x,y) = Max { a[i]+a[i+1]+…+a[j] ; x ≤ i ≤ j ≤ y }.
Given M queries, your program must output the results of these queries.
Input
The first line of the input file contains the integer N.
In the second line, N numbers follow.
The third line contains the integer M.
M lines follow, where line i contains 2 numbers xi and yi.
Output
Your program should output the results of the M queries, one query per line.
Example
Input:
3
-1 2 3
1
1 2
Output:
2
Solution
此题是要我们求给定区间的最大子段和,用线段树做就好了。不过有一个要好好处理的点是这一段最大子段和,有可能是跨越两个子区间,所以我们记录一下左起最大值,右起最大值。然后在pushup中每次更新一下。见代码吧(博主主要是觉得那个pushup很难相通,找了很久的博客,最后才明白)
一下讲解来自:https://www.cnblogs.com/ztz11/p/9343366.html
我们做这道题首先应该想的,是两个区间如何合并
很显然,合并后最大子段和,要么是原来左儿子的,要么是原来右儿子的
还有一种可能是左右两个儿子合并后在中间新生成的
所以,每个节点维护四个元素
分别表示他所管辖的区间和,他所管辖的区间中的最大连续子段和
从左起最大连续子段和(意思是子段的左端点一定是区间的左端点)
从右起最大连续子段和(意思同上)
此时我们可以这样转移:
sum(和)就不用说了
这一层的从左起最大连续子段和=max(左儿子的从左起最大连续子段和,左儿子的和+右儿子的从左起最大连续子段和)
这一层的从左起最大连续子段和方法同上
这一层的最大连续子段和=max(左儿子的最大连续子段和,右儿子的最大连续子段和,(左儿子从右起的最大连续字段和+右儿子从左起的最大连续子段和(也就是合并后生成的中间最大子段)))
OK,任务完成
查询同理
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define INF 0x3f3f3f3f
using namespace std;
struct node{
int lmax,rmax,max,sum;
//左起最大不一定是从最左端开始,而是从这个区间的某个点开始到最右边(我是这么理解的)
//右起同理
node(int x=0) {
sum=max=lmax=rmax=x;
}
}tree[1000001];
int ql,qr,n,m;
inline int read(){
int x=0,w=1;char ch;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
return x*w;
}
node pushup(node x,node y) {
node ans;
ans.sum=x.sum+y.sum;//区间和
ans.max=max(x.rmax+y.lmax,max(x.max,y.max));//左起最大+右起最大,两个子区间最大
ans.lmax=max(x.lmax,x.sum+y.lmax);
ans.rmax=max(y.rmax,y.sum+x.rmax);//同理
return ans;
}
void build(int rt,int l,int r) {
if(l==r) {
int x;
x=read();
tree[rt]=node(x);
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
tree[rt]=pushup(tree[ls],tree[rs]);
}
node query(int rt,int l,int r) {
if(ql<=l&&r<=qr) return tree[rt];
//线段树在这里有几种写法,找个时间总结一下,每种写法都有些特殊的用途
node x(-INF),y(-INF);
x.sum=y.sum=0;
int mid=(l+r)>>1;
if(ql<=mid) x=query(ls,l,mid);
if(qr>mid) y=query(rs,mid+1,r);
return pushup(x,y);
}
int main(){
freopen("gss1.in","r",stdin);
freopen("gss1.out","w",stdout);
while(~scanf("%d",&n)) {
build(1,1,n);
m=read();
while(m--) {
ql=read();qr=read();
printf("%d\n",query(1,1,n).max);
}
}
return 0;
}