zoukankan      html  css  js  c++  java
  • 题解【SP1043】 GSS1

    题目描述

    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.

    输入输出格式

    输入格式

    • 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 (x_i) and (y_i).

    输出格式

    Your program should output the results of the (M) queries, one query per line.

    输入输出样例

    输入样例#1

    3 
    -1 2 3
    1
    1 2
    

    输出样例#1

    2
    

    题意翻译

    给出了序列 (A_1,A_2,…,A_n(a_i≤15007,1≤N≤50000))

    查询定义如下: 查询 ((x,y)=max{a_i+a_{i+1}+...+a_j;x≤i≤j≤y})

    给定(M)个查询,程序必须输出这些查询的结果,每行一个查询。

    题解

    (SPOJ)(GSS)系列一共有(8)题,这(8)道题目都是有关数据结构的,与(Ynoi)类似。

    这是(SPOJ)(GSS)系列的第一题,考察的是用线段树求区间最大子段和 (本蒟蒻不会猫树)

    众所周知,线段树有以下基本的(3)个操作:(pushup)(bulid)(getans),这(3)个操作分别对应合并区间、建树的求答案。

    我们尝试用这三种操作解决这道题:

    首先,我们定义一个结构体:

    struct Node
    {
    	int sum, lans, rans, ans;
    } t[50005 << 2];
    

    其中,(sum)表示区间和,(lans)表示最大前缀和,(rans)表示最大后缀和,(ans)表示区间内的最大子段和,我们的目标是求出(x)~(y)区间内的(ans)

    然后,我们分析,如何进行(pushup)操作。

    易知,这个区间内的区间和就是它子区间的和加上它右子区间的和。

    区间最大前缀和是它左子区间最大子段和,与左子区间和加上右子区间的最大前缀和的最大值,最大后缀和同理。

    考虑如何合并区间最大子段和?

    经过分析,我们得出:区间最大子段和就是(max()左子区间的最大子段和,右子区间的最大子段和,
    左子区间的最大后缀+右子区间的最大前缀和())

    综上,我们就得出了(pushup)的代码:

    inline void pushup(int x)
    {
    	t[x].sum = t[x << 1].sum + t[(x << 1) | 1].sum;//求出区间和
    	t[x].lans = max(t[x << 1].lans, t[x << 1].sum + t[(x << 1) | 1].lans);//区间最大前缀和
    	t[x].rans = max(t[(x << 1) | 1].rans, t[(x << 1) | 1].sum + t[x << 1].rans);//区间最大后缀和
    	t[x].ans = max(max(t[x << 1].ans, t[(x << 1) | 1].ans), t[x << 1].rans + t[(x << 1) | 1].lans);//区间最大子段和
    }
    

    (build)操作与普通线段树的(build)操作一模一样。

    下面放出(build)操作的代码:

    void bulid(int s, int o, int p)
    {
    	if (s == o)//已经是叶子节点
    	{
    		t[p].sum = t[p].lans = t[p].rans = t[p].ans = gi();//输入并初始化叶子节点的成员
    		return;
    	}
    	int mid = (s + o) >> 1;//找出区间中点
    	bulid(s, mid, p << 1);//递归左子区间
    	bulid(mid + 1, o, (p << 1) | 1);//递归右子区间
    	pushup(p);//合并区间
    }
    

    (getans)操作同理。

    下面放出(AC)代码:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <cctype>
    
    using namespace std;
    
    inline int gi()
    {
        int f = 1, x = 0; char c = getchar();
        while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar();}
        while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar();}
        return f * x;
    }
    
    int n, m;
    struct Node
    {
    	int sum, lans, rans, ans;
    } t[50005 << 2];
    
    inline void pushup(int x)//合并操作
    {
    	t[x].sum = t[x << 1].sum + t[(x << 1) | 1].sum;
    	t[x].lans = max(t[x << 1].lans, t[x << 1].sum + t[(x << 1) | 1].lans);
    	t[x].rans = max(t[(x << 1) | 1].rans, t[(x << 1) | 1].sum + t[x << 1].rans);
    	t[x].ans = max(max(t[x << 1].ans, t[(x << 1) | 1].ans), t[x << 1].rans + t[(x << 1) | 1].lans);
    }
    
    void bulid(int s, int o, int p)//建树
    {
    	if (s == o)
    	{
    		t[p].sum = t[p].lans = t[p].rans = t[p].ans = gi();
    		return;
    	}
    	int mid = (s + o) >> 1;
    	bulid(s, mid, p << 1);
    	bulid(mid + 1, o, (p << 1) | 1);
    	pushup(p);
    }
    
    Node getans(int l, int r, int s, int o, int p)//求答案
    {
    	if (l <= s && r >= o)//如果包含区间
    	{
    		return t[p];//就直接返回
    	}
    	int mid = (s + o) >> 1;//求出中点
    	if (l > mid) return getans(l, r, mid + 1, o, (p << 1) | 1);//如果左端点在中点右边,就递归右区间
    	if (r <= mid) return getans(l, r, s, mid, p << 1);//如果右端点在中点左边,就递归左区间
    	else 
    	{
    		Node ans, a, b;
    		a = getans(l, r, s, mid, p << 1), b = getans(l, r, mid + 1, o, (p << 1) | 1);//求出左区间和右区间的各项参数
    		ans.sum = a.sum + b.sum;
    		ans.ans = max(max(a.ans, a.rans + b.lans), b.ans);
    		ans.lans = max(a.lans, a.sum + b.lans);
    		ans.rans = max(b.rans, b.sum + a.rans);//合并答案
    		return ans;//最后返回答案
    	}
    }
    
    int main()//进入主函数
    {
    	n = gi();//输入节点个数
    	bulid(1, n, 1);//建树
    	m = gi();//输入询问个数
    	for (int i = 1; i <= m; i++)
    	{
    		int x = gi(), y = gi();
    		printf("%d
    ", getans(x, y, 1, n, 1).ans);//求出答案
    	}
    	return 0;//结束
    }
    
  • 相关阅读:
    从与计算机结缘说起
    个人作业2:APP案例分析
    团队作业4——第一次项目冲刺(Alpha版本)第二篇
    团队项目作业1团队展示与选题
    团队作业4——第一次项目冲刺(Alpha版本)第三篇
    团队作业3——需求改进&系统设计
    技术博客
    技术博客二
    bootstrap前端框架使用总结分享
    ADO.NET Entities Framework 的增删查改(我自己写的,可以作为范例)
  • 原文地址:https://www.cnblogs.com/xsl19/p/11130520.html
Copyright © 2011-2022 走看看