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. Scrambled Polygon--poj2007(极角排序模板)

    http://poj.org/problem?id=2007 #include<stdio.h> #include<math.h> #include<algorithm& ...

  2. [Bzoj4832][Lydsy2017年4月月赛]抵制克苏恩 (期望dp)

    4832: [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 673  Solved: 261[Submit][ ...

  3. 关键字检索高亮标出-javasript/jQuery代码实现

    原文:http://www.open-open.com/code/view/1454504432089 此方法传入2个参数,一个是被检索内容所在的表单或者HTML元素的ID,另一为关键字,多个关键字的 ...

  4. Oracle 用户表空间查看、修改大小、设置自增长等

      分类: Oracle 首先登入某个用户,查看某个用户在哪个表空间下: select username,default_tablespace from user_users; 查看该用户下所有的表: ...

  5. vim配置为IDE环境(超详细,极力推荐 git)

    https://github.com/yangyangwithgnu/use_vim_as_ide 1. 用法 git clone https://github.com/VundleVim/Vundl ...

  6. Codeforces div.2 B. The Child and Set

    题目例如以下: B. The Child and Set time limit per test 1 second memory limit per test 256 megabytes input ...

  7. 【Sublime】Sublime Text 2集成TortoiseSVN插件

    作者:zhanhailiang 日期:2014-09-30 1. 下载TortoiseSVN.将其安装在默认位置: 2. 使用Sublime包管理器下载安装TortoiseSVN Plugin,安装后 ...

  8. Ubuntu下配置Tomcat以指定(非root)身份执行

    My Blog:http://www.outflush.com/ 通常情况下.在配置Tomcat生产环境时,一般会配置Tomcat以特定的身份执行(非root).这样有利于提高安全性,防止站点被黑后的 ...

  9. xcode10的那些事

    前言 这里主要介绍一下Xcode10 版本主要更新的内容.随着iOS12的发布,Xcode10已经可以从Mac App Store下载.Xcode10包含了iOS12.watchOS 5.macOS1 ...

  10. PHP 7.2 RC3 on CentOS/RHEL 7.3 via Yum

    https://webtatic.com/packages/php72/ rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-lat ...