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的更多相关文章

  1. SPOJ - GSS1 —— 线段树 (结点信息合并)

    题目链接:https://vjudge.net/problem/SPOJ-GSS1 GSS1 - Can you answer these queries I #tree You are given ...

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

    这一篇博客将教你什么? 如何用LCT打延迟标记,LCT和线段树延迟标记间的关系,为什么延迟标记要这样打. ——正片开始—— 学习这一篇博客前,确保你会以下知识: Link-Cut-Tree,普通线段树 ...

  3. SPOJ 2713 线段树(sqrt)

    题意:       给你n个数(n <= 100000),然后两种操作,0 x y :把x-y的数全都sqrt ,1 x y:输出 x-y的和. 思路:       直接线段树更新就行了,对于当 ...

  4. SPOJ1716 GSS3(线段树)

    题意 Sol 会了GSS1,GSS3就比较无脑了 直接加个单点修改即可,然后update一下 /* */ #include<cstdio> #include<cstring> ...

  5. SP1716 GSS3(线段树+矩阵乘法)

    Code: #include <bits/stdc++.h> #define N 50001 #define ll long long #define lson now<<1 ...

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

    A Simple Problem with Integers Time Limit:5000MS   Memory Limit:131072K Case Time Limit:2000MS Descr ...

  7. POJ 3468 线段树 成段更新 懒惰标记

    A Simple Problem with Integers Time Limit:5000MS   Memory Limit:131072K Case Time Limit:2000MS Descr ...

  8. 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 ...

  9. SPOJ GSS3 Can you answer these queries III ——线段树

    [题目分析] GSS1的基础上增加修改操作. 同理线段树即可,多写一个函数就好了. [代码] #include <cstdio> #include <cstring> #inc ...

随机推荐

  1. makefile的语法及写法(二)

     3 Makefile书写规则 -------------------------------------------------------------------------------- 规则包 ...

  2. json数组原始字符串

    var a = '{"name":"1234"}';var c = '{["name":"张三","age&q ...

  3. java验证身份证号码是否有效源代码

    原文:http://www.open-open.com/code/view/1420373343171 1.描述 用java语言判断身份证号码是否有效,地区码.出身年月.校验码等验证算法 2.源代码 ...

  4. Bootstrap的js插件之弹出框(popover)

    data-toggle="popover"--使弹出框可以切换状态: title--设置弹出框的标题: data-content--设置弹出框的内容部分: data-placeme ...

  5. &lt;Android&gt;greenrobot-EventBus,guava-Event Bus的异步实现

    刚開始是从otto入手,可是otto不支持异步运行.所以后来才開始研究了Event Bus.关于Event Bus,先前搜索的时候,看到网上的实例,非常碎,并且非常多都是一样的内容,代码看下来基本上是 ...

  6. CSS多种方法实现分隔线

    <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8" /> <title&g ...

  7. performSelector调用和直接调用的区别

    今天在准备出笔试题的过程中随便搜了一下其他的笔试题,看到其中一个就是关于performSelector与直接调用的区别. 个人感觉这其实是一个陷阱题,因为大部分应用场景下,用哪一种都可以,可以说是没有 ...

  8. LoadRunner 比较字符串是否相等

    int strcmp ( const char *string1, const char *string2 );大小写敏感.int stricmp ( const char *string1, con ...

  9. Android——SlidingMenu学习总结

    来源 SlidingMenu是github上比較火开源库.很强大,不但但是简单的设置实现两側滑动菜单,还能够设置菜单的阴影.渐变色.划动模式等. 下载地址:https://github.com/jfe ...

  10. UISlider无法拖动进度条的问题解决

    UISlider无法拖动进度条的问题解决 最近业务中的视频播放使用到了UISlider,但是有一个奇怪的问题,就是在Modar出来的控制器中UISlider是可以正常使用的,但是在Push出来的控制器 ...