(CDOJ 844 线段树区间最大连续和 )<线段树的各种姿势>
来源:互联网 发布:百度域名注册服务费用 编辑:程序博客网 时间:2024/05/22 14:18
Problem
给定一个序列,每个点有相应权值
有两种操作:
①修改某个点的权值
②查询给定区间的最大连续区间和
Solution
- 首先要理解清楚题意,是在给定区间内求最大连续子序列。所以暴力的方法很简单,每次在给定区间上贪心地求即可。复杂度
O(m∗n) - 优化一点可以用线段树
- 线段树上维护4个值:区间总和s,从左端起的最大连续区间和ls,从右端起的最大连续区间和rs,整个区间最终的最大连续区间和ms。
- pushup操作
ⅰ ls的修改
可以更新为左子树的ls,或者左子树的s+右子树的ls,分别对应第一条红线和第二条红线
ⅱ rs的修改
同ls的更新一样,可以更新为右子树的rs,或者右子树的s+左子树的ls
目的是保证从端点开始
ⅲ ms的修改
正常的情况是更新为左右子树的max(ms),但是有一种特殊情况:
左子树的rs和右子树的ls是连续的,因此有可能左子树的rs+右子树的ls的值更大
所以一共三种情况
ⅳ s的修改
很简单,把左右子树的s加起来即可
一般来说求最值不需要记录区间和,但这个问题涉及到区间的交并,s可以用于解决完全包含某个子区间的问题(如ls,rs的修改时)
- query是这个题最难的部分
ⅰ 同样要维护4个全局变量:区间最大连续子序列anss(即最终答案),从左端点开始的最大连续子序列ansl,从右端点开始的最大连续子序列ansr,已经更新过的区间和sum(同样需要区间和来帮助询问答案)
ⅱ 线段树一个很重要的性质是:每次更新或者询问一定是先处理左子树,后处理右子树,因此sum维护的是从询问区间最左端到当前区间的区间和,利用sum可以很巧妙的维护其它3个的值
ⅲ 具体实现的方法
if(ll<=L&&rr>=R){ ansl=max(ansl,sum+t[pos].ls);//不更新,或者更新为:左面所有的值的和sum+当前区间的ls,利用线段树更新的顺序是先左后右,可以确保ansl维护的值是从最左端开始的 anss=max(ansl,anss); anss=max(anss,t[pos].ms); anss=max(anss,ansr+t[pos].ls); ansr=max(ansr+t[pos].s,t[pos].rs);//同样利用sum的性质更新 anss=max(ansr,anss); sum+=t[pos].s; return; }
ⅳ 最后需要注意的是,ansr的更新必须放在anss之后,因为如果先更新ansr,那么ansr的值有可能会包含当前区间的值,这样会造成重复
Code
// by spli#include<cstring>#include<cstdio>#include<iostream>#include<algorithm>#define LL long longusing namespace std;const int N=500010;const LL inf=100000000ll;int n,m;struct seg{ int L,R; LL s,ms,ls,rs;}t[N*4+5];LL ansl=-inf,ansr=-inf,anss=-inf,sum=0;void pushup(int pos){ t[pos].s=t[pos<<1].s+t[pos<<1|1].s; t[pos].ms=max(t[pos<<1].ms,max(t[pos<<1|1].ms,t[pos<<1].rs+t[pos<<1|1].ls)); t[pos].ls=max(t[pos<<1].ls,t[pos<<1].s+t[pos<<1|1].ls); t[pos].rs=max(t[pos<<1|1].rs,t[pos<<1|1].s+t[pos<<1].rs);}void build(int pos,int L,int R){ t[pos].L=L; t[pos].R=R; if(L==R){ scanf("%lld",&t[pos].s); t[pos].ms=t[pos].ls=t[pos].rs=t[pos].s; return; } int mid=(L+R)>>1; build(pos<<1,L,mid); build(pos<<1|1,mid+1,R); pushup(pos);}void update(int pos,int k,int v){ int L=t[pos].L; int R=t[pos].R; if(L==R){ t[pos]=(seg){L,R,v,v,v,v}; return; } int mid=(L+R)>>1; if(k<=mid) update(pos<<1,k,v); else update(pos<<1|1,k,v); pushup(pos);}void query(int pos,int ll,int rr){ int L=t[pos].L; int R=t[pos].R; if(ll>R||L>rr) return; if(ll<=L&&rr>=R){ ansl=max(ansl,sum+t[pos].ls); anss=max(ansl,anss); anss=max(anss,t[pos].ms); anss=max(anss,ansr+t[pos].ls); ansr=max(ansr+t[pos].s,t[pos].rs); anss=max(ansr,anss); sum+=t[pos].s; return; } int mid=(L+R)>>1; query(pos<<1,ll,rr); query(pos<<1|1,ll,rr);}int main(){ scanf("%d%d",&n,&m); build(1,1,n); int k,x,y; for(int i=1;i<=m;++i){ scanf("%d%d%d",&k,&x,&y); if(k==1){ anss=ansl=ansr=-inf;sum=0; query(1,x,y); printf("%lld\n",anss); } if(k==2) update(1,x,y); } return 0;}
阅读全文
0 0
- (CDOJ 844 线段树区间最大连续和 )<线段树的各种姿势>
- CDOJ 844 线段树区间最大连续和
- uva1400 区间最大连续和 线段树
- hdu1540(线段树求连续区间最大和)
- POJ3667【线段树、最大连续区间】
- hdu-1540(线段树+最大连续区间)
- LA3938 线段树 动态求区间最大连续和
- !SPOJ 1043 多次查询区间最大连续和-线段树
- HUST1722(线段树维护区间最大连续和)
- 【线段树-区间更新求区间和】CDOJ 1057
- poj 1750Potted Flower(线段树 区间合并 动态规划 区间求最大连续和)
- 求区间连续不超过K段的最大和--线段树+大量代码
- Vijos 题目P1083小白逛公园(线段树,区间连续最大和)
- POJ 题目2750 Potted Flower(线段树求环型区间中连续区间的最大和)
- HDU 1540(线段树,以一点扩展开的最大连续区间)
- I - Tunnel Warfare HDU 1540(线段树+最大的连续区间)
- HDU1540--线段树(最长连续区间)
- 【例题】【线段树】连续区间
- 物理服务器、VPS、云服务器、虚拟主机
- $RANDOM: 产生随机整数及双圆括号结构
- 南阳oj116--士兵杀敌(二)(线段树,更新,求和)
- 【原创】【百度之星2017初赛A】1001 小C的倍数问题
- 1.SoC时钟系统简介
- (CDOJ 844 线段树区间最大连续和 )<线段树的各种姿势>
- 别老扯什么Hadoop了,你的数据根本不够大
- Centos 上软raid 介绍和配置
- Spring4.X + Spring MVC + Mybatis3 零配置应用开发框架搭建详解(4)
- 1015. 德才论 (25)
- ssm之九 批量导入excel到数据库
- 大赞 《战狼2》破40亿 刷新华语票房纪录
- 零拷贝问题
- JS DOM