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节点开始调入进行操作,对于单点修改来说,我们从顶向下寻找这个点的叶子节点,之后向上反,修改与这个点相关的线段的全部信息。在本题中代码长这样。
void change(int p,int x,int v)
{
if(t[p].left==t[p].right)
{
t[p].sum=t[p].maxx=t[p].lmax=t[p].rmax=v;
return;
}
int mid=(t[p].left+t[p].right)>>;
if(x<=mid) change(p*,x,v);
else change(p*+,x,v);
renew(p);
}
找到叶子节点后,修改它除左右端点的全部信息为要修改成的值v。在未找到叶子节点之前,我们可以运用二分的思想来找我们需要的节点。
另外,没有人对renew函数有疑问嘛?提前剧透一下好了。renew就是在更新非叶子节点的信息。
void renew(int p)
{
t[p].sum=t[p*].sum+t[p*+].sum;
t[p].lmax=max(t[p*].lmax,t[p*].sum+t[p*+].lmax);
t[p].rmax=max(t[p*+].rmax,t[p*+].sum+t[p*].rmax);
t[p].maxx=max(max(t[p*].maxx,t[p*+].maxx),t[p*].rmax+t[p*+].lmax);
}
sum等于左右儿子的sum和,这很好理解。
lmax和rmax是什么鬼??我觉得(大概)可以这样理解:因为线段树中非叶子节点的信息都是由它的两个儿子节点维护得到的,那么对于一个非叶子节点的从左顶点出发的最大子段和(lmax),可以看做左孩子的lmax与 和右孩子有关的lmax 比较取最大值得到。那么和右孩子有关的lmax如何求出?由于要保证这个非叶子节点lmax从左端出发的性质,那么和右孩子有关的lmax=左孩子的区间和+右孩子的lmax。
那么rmax的维护同理啦。
maxx的维护思想类似,我们需要比较三个最大值。左孩子的maxx,右孩子的maxx,在中间合并交界的maxx即左孩子的rmax+右孩子的lmax。
那么我们搞一搞毒瘤的1 操作。
这里用到了结构体函数,但是“SegmentTree a,b,c"意思是SegmentTree被我们新发明了一种变量类型(如int,longlong,等)。只不过我们通常定义结构体时往往需要多个n元组,于是就开了数组。但其实开一个也是可以的。然后这就相当于一个n元组(stl中的pair,pair是二元组)。
这部分的讲解写在代码里。
Code
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[];
struct SegmentTree{
int left,right,lmax,rmax,sum,maxx;
}t[];
void renew(int p)
{
t[p].sum=t[p*].sum+t[p*+].sum;
t[p].lmax=max(t[p*].lmax,t[p*].sum+t[p*+].lmax);
t[p].rmax=max(t[p*+].rmax,t[p*+].sum+t[p*].rmax);
t[p].maxx=max(max(t[p*].maxx,t[p*+].maxx),t[p*].rmax+t[p*+].lmax);
}
void build(int p,int l,int r)
{
t[p].left=l,t[p].right=r;
if(l==r)
{
t[p].sum=t[p].maxx=t[p].lmax=t[p].rmax=a[l];
return;
}
int mid=(l+r)>>;
build(p*,l,mid);
build(p*+,mid+,r);
renew(p);//信息在初始建树时就应开始维护
}
void change(int p,int x,int v)
{
if(t[p].left==t[p].right)
{
t[p].sum=t[p].maxx=t[p].lmax=t[p].rmax=v;
return;
}
int mid=(t[p].left+t[p].right)>>;
if(x<=mid) change(p*,x,v);
else change(p*+,x,v);
renew(p);
}
SegmentTree ask(int p,int x,int y)
{//开始和普通的线段树区间查询写法没有什么不同
if(x<=t[p].left&&y>=t[p].right) return t[p];
int mid=(t[p].left+t[p].right)>>;
SegmentTree a,b,c;//c是我们要最终返回的六元组
a.sum=a.maxx=a.lmax=a.rmax=-0x3f3f3f3f;
b.sum=b.maxx=b.lmax=b.rmax=-0x3f3f3f3f;
c.sum=;
if(x<=mid)
{//递归实现
a=ask(p*,x,y);//a记录当前节点左子树的信息
c.sum+=a.sum;
}
if(y>mid)
{
b=ask(p*+,x,y);//b记录当前节点右子树的信息
c.sum+=b.sum;
}
//与之前renew中的维护方法同理
c.maxx=max(max(a.maxx,b.maxx),a.rmax+b.lmax);
c.lmax=max(a.lmax,a.sum+b.lmax);
c.rmax=max(b.rmax,b.sum+a.rmax);
//处理特例
if(x>mid) c.lmax=max(c.lmax,b.lmax);
if(y<=mid) c.rmax=max(c.rmax,a.rmax);
return c;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
build(,,n);
scanf("%d",&m);
for(int i=;i<=m;i++)
{
int op=;
scanf("%d",&op);
if(op==)
{
int x=,y=;
scanf("%d%d",&x,&y);
printf("%d\n",ask(,x,y).maxx);
//函数返回的是一个 六元组
}
if(op==)
{
int x=,v=;
scanf("%d%d",&x,&v);
change(,x,v);
}
}
return ;
}
最后小总结一发。
“从这道题目我们也可以看出,线段树作为一种比较通用的数据结构,能够维护各式各样的信息,前提是这些信息容易按照区间划分合并(又称满足区间可加性),我们只需要在父子传递信息和更新答案时稍作变化即可。"
--lydrainbowcat
SPOJ GSS3 线段树系列1的更多相关文章
- SPOJ - GSS1 —— 线段树 (结点信息合并)
题目链接:https://vjudge.net/problem/SPOJ-GSS1 GSS1 - Can you answer these queries I #tree You are given ...
- [线段树系列] LCT打延迟标记的正确姿势
这一篇博客将教你什么? 如何用LCT打延迟标记,LCT和线段树延迟标记间的关系,为什么延迟标记要这样打. ——正片开始—— 学习这一篇博客前,确保你会以下知识: Link-Cut-Tree,普通线段树 ...
- SPOJ 2713 线段树(sqrt)
题意: 给你n个数(n <= 100000),然后两种操作,0 x y :把x-y的数全都sqrt ,1 x y:输出 x-y的和. 思路: 直接线段树更新就行了,对于当 ...
- SPOJ1716 GSS3(线段树)
题意 Sol 会了GSS1,GSS3就比较无脑了 直接加个单点修改即可,然后update一下 /* */ #include<cstdio> #include<cstring> ...
- SP1716 GSS3(线段树+矩阵乘法)
Code: #include <bits/stdc++.h> #define N 50001 #define ll long long #define lson now<<1 ...
- 【POJ】3468 A Simple Problem with Integers ——线段树 成段更新 懒惰标记
A Simple Problem with Integers Time Limit:5000MS Memory Limit:131072K Case Time Limit:2000MS Descr ...
- POJ 3468 线段树 成段更新 懒惰标记
A Simple Problem with Integers Time Limit:5000MS Memory Limit:131072K Case Time Limit:2000MS Descr ...
- SPOJ GSS3 Can you answer these queries III[线段树]
SPOJ - GSS3 Can you answer these queries III Description You are given a sequence A of N (N <= 50 ...
- SPOJ GSS3 Can you answer these queries III ——线段树
[题目分析] GSS1的基础上增加修改操作. 同理线段树即可,多写一个函数就好了. [代码] #include <cstdio> #include <cstring> #inc ...
随机推荐
- 【永久激活,视频教程,超级详细】IntelliJ idea 2018.3安装+激活+汉化
简介 IDEA 全称IntelliJ IDEA,是用于java语言开发的集成环境(也可用于其他语言),IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手.代码自动提示.重构. ...
- mysql.bat
cls @echo off :设置窗口字体颜色 color 0a :设置窗口标题 TITLE MySQL管理程序 call :checkAdmin goto menu :菜单 :men ...
- HttpUtils 用于进行网络请求的工具类
原文:http://www.open-open.com/code/view/1437537162631 import java.io.BufferedReader; import java.io.By ...
- topcoder srm 550
div1 250pt: 题意:有个机器人,从某一点出发,他只有碰到地形边缘或者碰到走过的点时才会改变运动方向,然后接着走,现在给出他的运动轨迹,判断他的运动是否合法,如果合法的话,那么整个地形的最小面 ...
- OpenGL蓝宝书第七章:立体天空和纹理折射、双纹理(下)
对照了蓝宝书,才知道红宝书的长处. reflect函数的原理在红宝书中有说明,仅仅有对照了红宝书,才知道红宝书的定位:高级工具书. 蓝宝书作为入门级书籍,以较快的速度让读者敲到代码去思考,总遗留了须要 ...
- 小心入怀Yaf
http://www.laruence.com/2015/05/14/3021.html, 鸟哥文章 看了这个文章,更加要小心的对待 Yaf Yar 框架了. 不正确 glib 有一定的了解进来就是深 ...
- Linux如何更新软件源
Linux软件源的设置方法 1 打开数据源配置文件 vi /etc/apt/sources.list 添加相关的数据源,可以选择以下的数据源,不要写太多,否则会影响更新速度. 之后使用ap ...
- webpack-Modules(模块)
模块(Modules) 在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块. 每个模块具有比完整程序更小的接触面,使得校验. ...
- 【OpenCV新手教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...
- 使用命令行工具提升cocos2d-x开发效率 之CocosBuilder篇
http://www.cnblogs.com/flyFreeZn/p/3617983.html 假设你正在使用CocosBuilder或者是其它基于CocosBuilder源代码改装而成的工具为你的游 ...