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. 继续畅通工程--hdu1879(最小生成树 模板题)

    http://acm.hdu.edu.cn/showproblem.php?pid=1879 刚开始么看清题  以为就是n行  后来一看是n*(n-1)/2行   是输入错误  真是够够的 #incl ...

  2. UVA 3882【dp】【简单数学】

    题意: 给定三个数分别是: 人数    间隔     起点 题目中人的编号从1开始.在进行约瑟夫环的判定之后,求解最后能够活下来的人. 思路: 约瑟夫环的递推公式是 f[n]=(f[n-1]+jian ...

  3. 2016 ACM-ICPC CHINA-Final

    补题进度:10/12 地址:http://codeforces.com/gym/101194 A(签到) 略 B(数位DP) 题意: 定义一个01字符串为good串当且仅当将其奇数位或者偶数位单独拎出 ...

  4. Tomcat+Servlet登录页面实例

    概念   Tomcat server是一个免费的开放源码的Web 应用server,属于轻量级应用server,在中小型系统和并发訪问用户不是非常多的场合下被普遍使用,是开发和调试JSP 程序的首选. ...

  5. 202. Segment Tree Query

    最后更新 二刷 09-Jan-17 正儿八经线段树的应用了. 查找区间内的值. 对于某一个Node,有这样的可能: 1)需要查找区间和当前NODE没有覆盖部分,那么直接回去就行了. 2)有覆盖的部分, ...

  6. IntelliJ IDEA 基本配置入门

    前言:今天下载安装IntelliJ IDEA.随手创建了一个项目,运行Build提示错误. 与大多数用于开发JAVA的IDE类似,不做不论什么配置.编译是不会成功的.因此我尝试对IDEA的配置进行了一 ...

  7. LUA协程复用

    -----协程复用根函数 local function routine(fun, args) while (fun) do fun, args = coroutine.yield(fun(table. ...

  8. Mybatis加入Ehcache支持

    1.Mybatis默认的缓存配置 MyBatis 包括一个很强大的查询缓存特性,它能够很方便地配置和定制. Mybatis缓存包括全局的缓存和局部的缓存.全局的缓存能够讲主配置文件的setting属性 ...

  9. JS日历控件 灵活设置: 精确的时分秒.

     在今年7月份时候 写了一篇关于 "JS日历控件" 的文章 , 当时仅仅支持 年月日 的日历控件,如今优化例如以下:      1. 在原基础上 支持 yyyy-mm-dd 的年月 ...

  10. [LeetCode]Two Sum 【Vector全局指针的使用】

    无序数组返回两个元素和为给定值的下标. tricks:无序.返回下标增序.返回的是原始数组的下标. vector<int>*pa; bool cmp(int x,int y){ retur ...