SPOJ GSS3 线段树系列1

SPOJ GSS系列真是有毒啊!

立志刷完,把线段树搞完!

来自lydrainbowcat线段树上的一道例题。(所以解法参考了lyd老师)

题意翻译

n 个数, q 次操作

操作0 x y把 Ax 修改为 y

操作1 l r询问区间 [l,r] 的最大子段和

数据规模在50000,有负数。

冷静分析

因为要维护最大子段和,那么我们可以在线段树struct中维护这么几个信息:

sum(区间和)、lmax(从左顶点出发的最大子段和)、rmax(从右顶点出发的最大子段和)、maxx(这段的最大子段和)以及常规的左端点left右端点right。

0操作还是比较容易的,是线段树的单点修改。线段树的操作基本上都是从1节点开始调入进行操作,对于单点修改来说,我们从顶向下寻找这个点的叶子节点,之后向上反,修改与这个点相关的线段的全部信息。在本题中代码长这样。

 1 void change(int p,int x,int v)
 2 {
 3     if(t[p].left==t[p].right)
 4     {
 5         t[p].sum=t[p].maxx=t[p].lmax=t[p].rmax=v;
 6         return;
 7     }
 8     int mid=(t[p].left+t[p].right)>>1;
 9     if(x<=mid) change(p*2,x,v);
10     else change(p*2+1,x,v);
11     renew(p);
12 }

找到叶子节点后,修改它除左右端点的全部信息为要修改成的值v。在未找到叶子节点之前,我们可以运用二分的思想来找我们需要的节点。

另外,没有人对renew函数有疑问嘛?提前剧透一下好了。renew就是在更新非叶子节点的信息。

1 void renew(int p)
2 {
3     t[p].sum=t[p*2].sum+t[p*2+1].sum;
4     t[p].lmax=max(t[p*2].lmax,t[p*2].sum+t[p*2+1].lmax);
5     t[p].rmax=max(t[p*2+1].rmax,t[p*2+1].sum+t[p*2].rmax);
6     t[p].maxx=max(max(t[p*2].maxx,t[p*2+1].maxx),t[p*2].rmax+t[p*2+1].lmax);
7 }

sum等于左右儿子的sum和,这很好理解。

lmax和rmax是什么鬼??我觉得(大概)可以这样理解:因为线段树中非叶子节点的信息都是由它的两个儿子节点维护得到的,那么对于一个非叶子节点的从左顶点出发的最大子段和(lmax),可以看做左孩子的lmax与 和右孩子有关的lmax 比较取最大值得到。那么和右孩子有关的lmax如何求出?由于要保证这个非叶子节点lmax从左端出发的性质,那么和右孩子有关的lmax=左孩子的区间和+右孩子的lmax。

那么rmax的维护同理啦。

maxx的维护思想类似,我们需要比较三个最大值。左孩子的maxx,右孩子的maxx,在中间合并交界的maxx即左孩子的rmax+右孩子的lmax。

那么我们搞一搞毒瘤的1 操作。

这里用到了结构体函数

最后小总结一发。

“从这道题目我们也可以看出,线段树作为一种比较通用的数据结构,能够维护各式各样的信息,前提是这些信息容易按照区间划分合并(又称满足区间可加性),我们只需要在父子传递信息和更新答案时稍作变化即可。"

--lydrainbowcat

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9095087.html

时间: 2024-11-01 21:24:20

SPOJ GSS3 线段树系列1的相关文章

spoj GSS线段树以及二维树状数组合集

T1 维护lmax 向左延伸的最大值,rmax同理,sum区间和,ans答案. 转移见operator + #include<bits/stdc++.h> #define mid (l+(r-l)/2) #define ls (rt<<1) #define rs (rt<<1|1) #define int long long using namespace std; const int N =(int)1e5+10; struct TREE { int lef,rig,

spoj 1043 线段树

线段树在解决区间合并问题上还是很强力的,每个结点维护三个值:maxl, maxr, maxn,然后合并操作见pushup函数. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 50001; 7 int a[N]; 8 int sum[N]; 9 10 struct Node 11 { 12 int l, r; 1

【线段树】线段树系列 0.2单点修改区间求和线段树

1080 线段树练习 题目描述 Description 一行N个方格,开始每个格子里都有一个整数.现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和:修改的规则是指定某一个格子x,加上或者减去一个特定的值A.现在要求你能对每个提问作出正确的回答.1≤N<100000,,提问和修改的总数m<10000条. 输入描述 Input Description 输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数.接下一个正整数m,再接下来有m行,表示

spoj 2713 线段树

给定一个序列,有两种操作:对一个区间内的数字开方和求区间内所有数字的和.注意到一个即使很大的数经过没几次开方操作以后就会变成1,而1开方还是1.所以可以用线段树来维护,对于那些全部都是1的区间(即区间和等于区间长度)我们不用更新,剩下的就是区间求和了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 using namespace std;

【线段树】线段树系列 0.1单点修改单点求和线段树

终于搞定了单点修改线段树...3个月..操蛋..根本没有模板题..觉得太弱了...艹蛋...必须进一步更新我的版本啊 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; struct node { int left,right,value; }; node point[100]; int father[100]; int g; v

spoj GSS2 (线段树) - xgtao -

题目链接 这是一道线段树的题目,维护历史版本,给出N(<=100000)个数字(-100000<=x<=100000),要求求出在[l,r]区间里面的连续序列的最大值,并且重复的数字可以加入序列但是值不能再计算. 数据范围以及区间的查询提示使用线段树,但是我们怎么建树呢?在线操作是没法做的.那就离线吧. 定义s[i] = ai + ai+1 + ai+2 + ... an,以ai开头的数列的和,那么每次加入更新ai 那么s1,s2,...si都会相应的加一个ai,s[1~i]中出现过a[

[线段树系列] LCT打延迟标记的正确姿势

这一篇博客将教你什么? 如何用LCT打延迟标记,LCT和线段树延迟标记间的关系,为什么延迟标记要这样打. ——正片开始—— 学习这一篇博客前,确保你会以下知识: Link-Cut-Tree,普通线段树 当然,不会也没有关系,你可以先收藏这篇博客,等你学了以后再来看. 最好通过了这一道题:[模板]线段树Ⅱ 没有通过也没关系,对于本篇的知识只是一个启发作用. 我们平时使用的Link-Cut-Tree一般只需要打一个翻转标记rev[x]. 然后我们用pushR(x)函数来下发翻转标记. 那么我们现在来

SPOJ BGSHOOT 线段树

BGSHOOT - Shoot and kill The problem is about Mr.BG who is a great hunter. Today he has gone to a dense forest for hunting and killing animals. Sadly, he has only one bullet in his gun. He wants to kill as many animals as possible with only one bulle

【POJ】3468 A Simple Problem with Integers ——线段树 成段更新 懒惰标记

A Simple Problem with Integers Time Limit:5000MS   Memory Limit:131072K Case Time Limit:2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each