CODEVS 3981(求最大子段和+线段树)

题目链接:http://codevs.cn/problem/3981/

参考:https://blog.csdn.net/jokingcoder/article/details/81477253

一个区间的最大子段和有三种情况:

1.等于这个区间左儿子的最大子段和

2.等于这个区间右儿子的最大子段和

3.等于这个区间左儿子的后缀最大子段和+右儿子的前缀最大子段和

    tree[k].sumax = max(max(tree[l].sumax,tree[r].sumax),tree[l].rmax + tree[r].lmax);

一个区间前缀最大子段和有两种情况:

1.等于这个区间左儿子的前缀最大子段和

2.等于这个这儿子的前缀和加上右儿子的前缀最大子段和

    tree[k].lmax = max(tree[l].lmax,tree[l].sum + tree[r].lmax);

同样

一个区间的后缀最大子段和有两种情况:

1.等于这个区间右儿子的后缀最大子段和

2.等于这个区间右儿子的后缀和+左儿子的最大后缀和

    tree[k].rmax = max(tree[r].rmax,tree[r].sum + tree[l].rmax);
 1 #include <iostream>
 2 #include <cstring>
 3 #define mem(a,b) memset(a,b,sizeof(a));
 4 using namespace std;
 5 typedef long long ll;
 6 const int maxn = 500005;
 7 const ll INF = 0x3f3f3f3f;
 8 ll n,q,a[maxn];
 9 struct node{
10     ll sum,sumax,lmax,rmax;
11 }tree[maxn];
12 void pushup(ll k) {
13     ll l = k << 1, r = k << 1 | 1;
14     tree[k].sum = tree[l].sum + tree[r].sum;
15     tree[k].sumax = max(max(tree[l].sumax,tree[r].sumax),tree[l].rmax + tree[r].lmax);
16     tree[k].lmax = max(tree[l].lmax,tree[l].sum + tree[r].lmax);
17     tree[k].rmax = max(tree[r].rmax,tree[r].sum + tree[l].rmax);
18 }
19 void build(ll k,ll l,ll r) {
20     if(l == r) {
21         tree[k].lmax = tree[k].rmax = tree[k].sum = tree[k].sumax = a[l];
22         return ;
23     }
24     ll mid = (l + r) >> 1;
25     build(k << 1, l, mid);
26     build(k << 1 | 1,mid + 1, r);
27     pushup(k);
28 }
29 node Search(ll l,ll r,ll L,ll R,ll k) {
30     if(L <= l&& R >= r) {
31         return tree[k];
32     }
33     ll mid = (l + r) >> 1,ld = k << 1, rd = k << 1 | 1;
34     if(R <= mid) return Search(l,mid,L,R,ld);
35     if(L > mid) return Search(mid+1,r,L,R,rd);
36     node lo = Search(l,mid,L,R,ld), ro = Search(mid+1,r,L,R,rd),ans;
37     ans.sum = lo.sum + ro.sum;
38     ans.sumax = max(max(lo.sumax,ro.sumax),lo.rmax+ro.lmax);
39     ans.lmax = max(lo.lmax,lo.sum+ro.lmax);
40     ans.rmax = max(ro.rmax,ro.sum+lo.rmax);
41     return ans;
42 }
43 int main()
44 {
45     cin >> n;
46     for(ll i = 1; i <= n; i++) {
47         cin >> a[i];
48     }
49     build(1,1,n);
50     cin >> q;
51     ll x,y;
52     for(ll i = 1; i <= q; i++) {
53         cin >> x >> y;
54         cout << Search(1,n,x,y,1).sumax << endl;
55     }
56     return 0;
57 }

原文地址:https://www.cnblogs.com/LLLAIH/p/11327225.html

时间: 2024-10-06 10:50:41

CODEVS 3981(求最大子段和+线段树)的相关文章

codevs 3981 动态最大子段和(线段树)

题目传送门:codevs 3981 动态最大子段和 题目描述 Description 题目还是简单一点好... 有n个数,a[1]到a[n]. 接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和. 子段的意思是连续非空区间. 输入描述 Input Description 第一行一个数n. 第二行n个数a[1]~a[n]. 第三行一个数q. 以下q行每行两个数l和r. 输出描述 Output Description q行,每行一个数,表示a[l]到a[r]的最大子段和. 样

SPOJ 1043 Can you answer these queries I 求任意区间最大连续子段和 线段树

题目链接:点击打开链接 维护区间左起连续的最大和,右起连续的和.. #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <math.h> #include <vector> #include <map> using namespace std; #define N 50050 #define Lson

求逆序对(线段树版)

一个序列a1,a2,a3...aN,求出满足:ai > aj 且 i < j 的个数. 一个最容易想到的方法就是枚举所有的i,j看看是否满足,显然是O(n^2)的复杂度.不够好. 可以这样考虑,开一个数组保存这n个数出现的位置和对应的次数,这个数组要开到a数组里最大的那个数MAX,也就是hash,初始状态数组里没有元素,每个数对应的个数都是0. 如果考虑第i个数,找到比它大的所有的数 的个数,查找的范围即 ai+1~MAX,这就是到i这个位置的逆序对的总和,接着把a[i]这个数添加到数组里,也

codevs 3981 动态最大子段和

3981 动态最大子段和 http://codevs.cn/problem/3981/  题目等级 : 钻石 Diamond 题目描述 Description 题目还是简单一点好... 有n个数,a[1]到a[n]. 接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和. 子段的意思是连续非空区间. 输入描述 Input Description 第一行一个数n. 第二行n个数a[1]~a[n]. 第三行一个数q. 以下q行每行两个数l和r. 输出描述 Output Desc

SPOJ GSS系列 最大子段和 线段树+树链剖分+splay 1043 1557 1716 2713 2916 4487 6779

最大子段和的各种形式 题解内附每道题的 题意 题目链接 思路 SPOJ 1043 GSS1 静态区间求个最大子段和, 题解 SPOJ 1577 GSS2 和1一样,区别是若区间内存在相同的元素,则该元素只计算一次. 离线一下然后使劲跑.. 题解 SPOJ 1716 GSS3 和1一样,就是要支持单点修改 题解 SPOJ 2713 GSS4 ==普通线段树,感觉和这系列关系不大. 题解 SPOJ 2916 GSS5 题意有点怪,,跟3差不多,就是稍加强了点询问 题解 SPOJ 4487 GSS6

p1115 最大子段和(线段树)

题目描述-->p1115 最大子段和 虽然是一个普及-的题,但我敲了线段树 qwq 数组定义 \(lsum[ ]\)代表 该区间左端点开始的最大连续和. \(rsum[ ]\)代表 该区间右端点开始的最大连续和. \(ssum[ ]\)代表 区间内最大连续和. \(sum[ ]\) 代表区间和. Que and A Q:已知一个区间的左右区间的最大连续和,如何合并? A:这个区间的最大连续和要么是左子区间的最大连续和,要么是右子区间的最大连续和. 要么是左子区间的最大右起子段和+右子区间的最大左

(c) hdu1394* (求逆序对数)(线段树)

(c) hdu1394 如在阅读本文时遇到不懂的部分,请在评论区询问,或跳转 线段树总介绍 线段树求逆序对数比较少见啊(归并排序多快啊...但是本文是讲解线段树写法...),何况这题还加了点别的玩意儿... 1. 本来这种题目要离散化的,可是体中保证了数列0~n-1. 2. 每次把首位放到最末,显然不能 每次都求逆序对 ,于是又到了推 倒 导时间. 由 a1 a2 ... an 变为 a2 a3 ... an a1 减少的逆序对数为 a2~an中比a1小的数的个数 增加的逆序对数为 a2~a1中

hdu5592 倒序求排列+权值线段树

这种题为什么要用到主席树啊..8说了,直接上代码 /* 1-n的排列,给定所有前缀的逆序对数量,要求恢复排列 首先能确定最后一个数是什么,然后倒序确定即可 开线段树找空位:如果Ai-Ai-1=k,说明pi前面有k个数比它要大,,即有i-k-1个数比它小, 那么pi排在第i-k位,线段树查询第i-k个空位,然后把这个空位填上即可 */ #include<bits/stdc++.h> using namespace std; #define maxn 50005 int n,a[maxn],ans

codevs 4163 求逆序对的数目 -树状数组法

4163 hzwer与逆序对 时间限制: 10 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description hzwer在研究逆序对. 对于数列{a},如果有序数对(I,j)满足:i<j,a[i]>a[j],则(i,j)是一对逆序对. 给定一个数列{a},求逆序对个数. 输入数据较大,请使用scanf代替cin读入. 输入描述 Input Description 第一行一个数n,表示{a}有n个元素. 接下来n个数,描述{a}. 输出描述 Output D