GSS 系列 1-8

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;
}

猜你喜欢

转载自blog.csdn.net/tangzhide123yy/article/details/81335220
1-8