\[Preface
\]

没有 Preface。

\[Description
\]

维护一个长度为 \(n\) 的数列 \(A\) ,需要支持以下操作:

  • 0 x y 将 \(A_x\) 改为 \(y\) 。

  • 1 x y 求 \(\max\limits_{x \leq l \leq r \leq y}{\sum_{i=l}^rA[i]}\) 。

\[Solution
\]

区间最大子段和 是一个非常经典的问题。

对于 整体最大子段和 来说,一般有 \(O(n)\) 的 贪心分治 做法,我们讨论的重点是 分治 做法。

\(~\)

假设当前需求解最大子段和的区间是 \([l,r]\) ,令 \(mid=\left\lfloor\dfrac{l+r}{2}\right\rfloor\)

我们套路地把 \([l,r]\) 分成 \([l,mid]\) 和 \([mid+1,r]\) ,来进行分治求解:

\(~\)

首先对于形如 \([l,l]\) 的区间,就十分好处理了,这里不多说。

\(~\)

接下来,考虑下最大子段和满不满足 区间可加性 ?(\([l,mid]\) 和 \([mid+1,r]\) 的最大子段和能否推至 \([l,r]\))

显然,只维护一个最大子段和,对 左子区间右子区间 的最大子段和取个 \(\max\) 是不能维护最大子段和的,因为其漏掉了 最大子段和同时包含左子区间和右子区间 的情况。

那么对于剩下的这一种情况,它是必定经过中点 \(mid\) 的,那这种情况的最大段就是 左子区间从右端点向左走的最大段右子区间从左端点向右走的最大段 的并集,其值为 左子区间的后缀最大子段和 \(+\) 右子区间的前缀最大字段和

那我们再维护 前 \(/\) 后 缀最大子段和 ,对其三者取 \(\max\) ,最大子段和就满足区间可加性了。

维护 前 \(/\) 后 缀最大子段和 依旧可以分成 经过 \(mid\) \(/\) 不经过 \(mid\) 来讨论。

以前缀最大子段和为例,若经过 \(mid\) ,则最大段为 左子区间右子区间从左端点向右走的最大段 的并集,其值为 左子区间和 \(+\) 右子区间的前缀最大子段和 ;若不经过 \(mid\) ,则最大段为 左子区间从左端点向右走的最大段 。二者取个 \(\max\) 即可。后缀最大子段和同理。

那我们再维护个 区间和 ,那 最大子段和前 \(/\) 后 缀最大子段和 就都满足区间可加性了。

至于 区间和 \(......\) ,这玩意直接加就行了。

(上述内容大家可以自己画图感性理解一下

\(~\)

求解过程

约定变量:

\(sum\) : 区间和

\(lmax\) : 区间前缀最大子段和

\(rmax\) : 区间后缀最大子段和

\(wmax\) : 区间最大子段和

设函数 \(ask(l,r)\) 求的是关于区间 \([l,r]\) 的一个四元组\((\) \(sum\), \(lmax\), \(rmax\), \(wmax\) \()\)

首先有一个递归边界 \(l=r\) ,此时这四个元素均为 \(A_l\) 。

那对于一般情况,令 \(lc=ask(l,mid),rc=ask(mid+1,r)\) ,则有:

\[self.sum=lc.sum+rc.sum
\]

\[self.lmax=\max(lc.lmax,lc.sum+rc.lmax)
\]

\[self.rmax=\max(rc.rmax,rc.sum+lc.rmax)
\]

\[self.wmax=\max(lc.wmax,rc.wmax,lc.rmax+rc.lmax)
\]

此时 \(self\) 即为 \(ask(l,r)\) 。

struct data{
int sum;
int lmax;
int rmax;
int wmax;
}; data ask(int l,int r)
{
data self;
if(l==r)
{
self.sum=self.lmax=self.rmax=self.wmax=A[l];
return self;
}
int mid=(l+r)/2;
data lc=ask(l,mid),rc=ask(mid+1,r);
self.sum=lc.sum+rc.sum;
self.lmax=max(lc.lmax,lc.sum+rc.lmax);
self.rmax=max(rc.rmax,rc.sum+lc.rmax);
self.wmax=max(max(lc.wmax,rc.wmax),lc.rmax+rc.lmax);
return self;
}

\(~\)

然后你会发现,若对于每个询问都调用一次 ask(l,r) 会稳稳 T 掉

那我 bb 这么多有什么用呢

大家仔细想想,这个分治的过程像不像某个数据结构呢?

线段树?

线段树!

是的,用线段树维护,每个节点保存的是该节点所代表的区间 \([l,r]\) 的 \((\) \(sum\), \(lmax\), \(rmax\), \(wmax\) \()\) 。

剩下的都是一些线段树基本操作了。

\[Code
\]

#include<cstdio>
#include<algorithm> #define RI register int using namespace std; const int SIZE=500100; int n,m;
int a[SIZE]; struct SegmentTree{
int l,r;
int sum;
int lmax;
int rmax;
int dat;
}t[SIZE*4]; void build(int p,int l,int r)
{
t[p].l=l;t[p].r=r;
if(l==r){t[p].sum=t[p].lmax=t[p].rmax=t[p].dat=a[l];return;}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].sum=t[p*2].sum+t[p*2+1].sum;
t[p].lmax=max(t[p*2].lmax,t[p*2].sum+t[p*2+1].lmax);
t[p].rmax=max(t[p*2+1].rmax,t[p*2+1].sum+t[p*2].rmax);
t[p].dat=max(max(t[p*2].dat,t[p*2+1].dat),t[p*2].rmax+t[p*2+1].lmax);
} void change(int p,int x,int val)
{
if(t[p].l==t[p].r){t[p].sum=t[p].lmax=t[p].rmax=t[p].dat=val;return;}
int mid=(t[p].l+t[p].r)/2;
if(x<=mid)change(p*2,x,val);
else change(p*2+1,x,val);
t[p].sum=t[p*2].sum+t[p*2+1].sum;
t[p].lmax=max(t[p*2].lmax,t[p*2].sum+t[p*2+1].lmax);
t[p].rmax=max(t[p*2+1].rmax,t[p*2+1].sum+t[p*2].rmax);
t[p].dat=max(max(t[p*2].dat,t[p*2+1].dat),t[p*2].rmax+t[p*2+1].lmax);
} SegmentTree ask(int p,int l,int r)
{
if(l<=t[p].l&&t[p].r<=r)return t[p];
int mid=(t[p].l+t[p].r)/2;
if(l<=mid&&mid<r)
{
SegmentTree lc=ask(p*2,l,r),rc=ask(p*2+1,l,r),self;
self.sum=self.lmax=self.rmax=self.dat=0;
self.sum=lc.sum+rc.sum;
self.lmax=max(lc.lmax,lc.sum+rc.lmax);
self.rmax=max(rc.rmax,rc.sum+lc.rmax);
self.dat=max(max(lc.dat,rc.dat),lc.rmax+rc.lmax);
return self;
}
if(l<=mid)
return ask(p*2,l,r);
if(mid<r)
return ask(p*2+1,l,r);
} int main()
{
scanf("%d",&n);
for(RI i=1;i<=n;i++)
scanf("%d",&a[i]); build(1,1,n); scanf("%d",&m); while(m--)
{
char op[2];
int l,r;
scanf("%s%d%d",op,&l,&r); switch(op[0])
{
case '1':{
if(l>r)swap(l,r);
printf("%d\n",ask(1,l,r).dat);
break;
} case '0':{
change(1,l,r);
break;
}
}
}
return 0;
}

\[Thanks \ for \ watching
\]

题解 SP1716 【GSS3 - Can you answer these queries III】的更多相关文章

  1. 线段树 SP1716 GSS3 - Can you answer these queries III

    SP1716 GSS3 - Can you answer these queries III 题意翻译 n 个数,q 次操作 操作0 x y把A_xAx 修改为yy 操作1 l r询问区间[l, r] ...

  2. SP1716 GSS3 - Can you answer these queries III 线段树

    问题描述 [LG-SP1716](https://www.luogu.org/problem/SP1716] 题解 GSS 系列的第三题,在第一题的基础上带单点修改. 第一题题解传送门 在第一题的基础 ...

  3. SP1716 GSS3 - Can you answer these queries III

    题面 题解 相信大家写过的传统做法像这样:(这段代码蒯自Karry5307的题解) struct SegmentTree{ ll l,r,prefix,suffix,sum,maxn; }; //.. ...

  4. SP1716 GSS3 - Can you answer these queries III(单点修改,区间最大子段和)

    题意翻译 nnn 个数, qqq 次操作 操作0 x y把 AxA_xAx​ 修改为 yyy 操作1 l r询问区间 [l,r][l, r][l,r] 的最大子段和 题目描述 You are give ...

  5. SP1716 GSS3 - Can you answer these queries III - 动态dp,线段树

    GSS3 Description 动态维护最大子段和,支持单点修改. Solution 设 \(f[i]\) 表示以 \(i\) 为结尾的最大子段和, \(g[i]\) 表示 \(1 \sim i\) ...

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

  7. 数据结构(线段树):SPOJ GSS3 - Can you answer these queries III

    GSS3 - Can you answer these queries III You are given a sequence A of N (N <= 50000) integers bet ...

  8. 题解【SP1716】GSS3 - Can you answer these queries III

    题目描述 You are given a sequence \(A\) of \(N (N <= 50000)\) integers between \(-10000\) and \(10000 ...

  9. 【SP1716】GSS3 - Can you answer these queries III(动态DP)

    题目链接 之前用线段树写了一遍,现在用\(ddp\)再写一遍. #include <cstdio> #define lc (now << 1) #define rc (now ...

随机推荐

  1. Javascript小白经典题型(一)

    1. 输出是什么? function sayHi() { console.log(name) console.log(age) var name = 'Lydia' let age = 21 } sa ...

  2. ArcGIS Server 10.1新特性系列---动态图层

    ArcGIS Server 10.1新特性里面有几个新功能是非常棒的,其中有一个就是动态图层.动态图层不是一种新发明的图层,而是说在arcgis server的web应用中可以动态的配置其渲染和内容功 ...

  3. InteliJ IDEA 启动应用出现 Error:failed to create a child event loop 解决方案

    1.首先检查自己电脑是否能够联网 2.打开电脑防火墙允许 InteliJ IDEA 进行通信如下图所示

  4. Python中with标签的使用详解

    1.在python DTL模板中,想要定义变量,可以通过"with"语句来实现. 2."with"语句有两种使用方式: 第一种是"with xx=xx ...

  5. 79.纯 CSS 创作单元素麦当劳金拱门 Logo(原文)

    1. 效果图: 效果地址:https://codepen.io/flyingliao/pen/JgavjX 原理:m是伪元素::before弄出来的,::after遮挡中间下方一小块. 感想:学到一个 ...

  6. Spring Boot2 系列教程 (十五) | 服务端参数校验之一

    估计很多朋友都认为参数校验是客户端的职责,不关服务端的事.其实这是错误的,学过 Web 安全的都知道,客户端的验证只是第一道关卡.它的参数验证并不是安全的,一旦被有心人抓到可乘之机,他就可以有各种方法 ...

  7. 使用 git 将代码推送到多个仓库

    使用 git 将代码推送到多个仓库 起因     起初,在 GitHub 建了一个仓库,200+ 的 commits .后来(终于在眼泪中明白...误

  8. 进一步学习 nox 教程,轻松掌握命令行用法

    英文 |Command-line usage 出处 | nox 官方文档 译者 | 豌豆花下猫@Python猫 Github地址:https://github.com/chinesehuazhou/n ...

  9. 固定表头的table

    在前端的开发过程中,表格时经常使用的一种展现形式.在我的开发过程中,当数据过多时,最常用的一种方式就是分页,但是有些地方还是需要滚动.通常的table 会随着滚动,导致表头看不见.一下是我找到的一种固 ...

  10. Docker 学习 1 入门

    Docker 学习 1 入门 dockert 安装. Mac Ubuntu 查看docker 版本 docker version 拉取image. docker pull e.g docker pul ...