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 ...
随机推荐
- P1420 最长连号
洛谷——P1420 最长连号 题目描述 输入n个正整数,(1<=n<=10000),要求输出最长的连号的长度.(连号指从小到大连续自然数) 输入输出格式 输入格式: 第一行,一个数n; 第 ...
- QT程序--小工具集合
这是在大一第一学期时写的参加程序设计大赛的作品,当时参加的时候仅仅只有贪吃蛇,迷宫算法和文件加密这三个功能,而且当时的界面并没有进行任何美化,现在想起来有点可惜.然而这并不是一个只写一遍的软件,在后期 ...
- SpringMVC Ueditor1.4.3 未找到上传数据
ueditor自事的fileupload组件与spring的有冲突.将那个类BinaryUploader 重写就可以了 return storageState; ...
- windows上安装mysql
安装mysql后 命令行闪退 查看服务 也没有MySQL服务启动 你安装了mysql没有,没有就先安装,安装好mysql以后,在bin目录下有个mysqld.exe,运行这个程序就可以添加mysql服 ...
- ln 软连接 & 硬连接
创建软连接的方式 #ln -s soure /file object 创建软连接是连接文件本身,可以跨分区建立软连接,不会应为不同分区而出现不能使用的问题. 在创建软连接的文件中,修改一处文件另一处同 ...
- Linux下获取线程TID的方法
如何获取进程的PID(process ID)? 可以使用: #include <unistd.h> pid_t getpid(void); 通过查看头文件说明,可以得到更详细的信息: fi ...
- 《The Swift Programming Language》的笔记-第28页
页 本页里的主要内容是: 1 介绍swift语言里的各类数制,或者说某一整数的不同表示形式. 1)十进制,无前缀 的序列 3)八进制,以0o开头.后边是若干0~7的序列 进制,以0x开头.后是0~9. ...
- my.cnf配置详解[转载]
先粘贴一份mac下的mysql5.6.22的配置文件 # Example MySQL config file for medium systems. # # This is for a system ...
- 用 kGDB 调试 Linux 内核
简介 这个文档记录了用kGDB调试Linux内核的全过程,都是在前人工作基础上的一些总结.以下操作都是基于特定板子来进行,但是大部分都能应用于其他平台. 要使用KGDB来调试内核,首先需要修改conf ...
- YTU 2440: C++习题 复数类--重载运算符+,-,*,/
2440: C++习题 复数类--重载运算符+,-,*,/ 时间限制: 1 Sec 内存限制: 128 MB 提交: 1189 解决: 774 题目描述 定义一个复数类Complex,重载运算符& ...