BZOJ5089 最大连续子段和(分块)
假设所有操作都是对整个序列的。考虑每个子区间,区间和与其被加的值构成一次函数关系。最大子段和相当于多个子区间取最大值,答案显然就在这些一次函数构成的下凸壳上。如果预处理出凸壳,只要在凸壳上暴力跳就可以回答询问了,因为加的都是正数,并且斜率不同的一次函数数量是O(n)的。暴力建凸壳的复杂度是O(n2)的。
那么考虑分块。每个块预处理出凸壳。区间加时,对于整块在凸壳上暴跳,零散部分暴力重构。查询时合并区间,类似于单点加的线段树做法,维护块内最大前缀和、最大后缀和。块大小取n1/3时最优。
造凸壳写挂调了1h,退役了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
#define NUM 2000
#define BLOCK 100
#define inf 10000000000000000ll
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,pos[N],num,block;
ll a[N],tmp[N],PRE[N],SUF[N];
struct line
{
ll k,b;
ll f(ll x){return k*x+b;}
};
ll cross(line x,line y){return (x.b-y.b-)/(y.k-x.k)+;}
struct hull
{
line a[BLOCK];int cnt,cur;
void ins(line x){while (cnt&&x.b>=a[cnt].b||cnt>&&x.f(cross(a[cnt],a[cnt-]))>a[cnt].f(cross(a[cnt],a[cnt-]))) cnt--;a[++cnt]=x;}
void clear(){cnt=,cur=,ins((line){,});}
void jump(ll x){while (cur<cnt&&a[cur].f(x)<a[cur+].f(x)) cur++;}
ll get(ll x){return a[cur].f(x);}
};
struct data
{
hull pre,suf,seq;int L,R;ll lazy,sum;
void build()
{
for (int i=L;i<=R;i++) a[i]+=lazy;lazy=;
sum=;for (int i=L;i<=R;i++) sum+=a[i];
ll x=;pre.clear();
for (int i=L;i<=R;i++) pre.ins((line){i-L+,x+=a[i]});
x=;suf.clear();
for (int i=R;i>=L;i--) suf.ins((line){R-i+,x+=a[i]});
seq.clear();
for (int k=;k<=R-L+;k++)
{
ll s=-inf;x=;for (int i=L;i<=L+k-;i++) x+=a[i];
for (int i=L+k-;i<=R;i++) s=max(s,x),x+=a[i+]-a[i-k+];
seq.ins((line){k,s});
}
}
void add(int x){sum+=1ll*(R-L+)*x,lazy+=x,pre.jump(lazy),suf.jump(lazy),seq.jump(lazy);}
ll maxpre(){return pre.get(lazy);}
ll maxsuf(){return suf.get(lazy);}
ll maxseq(){return seq.get(lazy);}
}f[NUM];
ll getseq(int l,int r)
{
ll s=,ans=;
for (int i=l;i<=r;i++)
{
s+=tmp[i];
if (s<) s=;
ans=max(ans,s);
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5089.in","r",stdin);
freopen("bzoj5089.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();block=*pow(n,1.0/)+;num=(n-)/block+;
for (int i=;i<=n;i++) a[i]=read();
for (int i=;i<=num;i++)
{
f[i].L=f[i-].R+,f[i].R=min(n,f[i].L+block-);
for (int j=f[i].L;j<=f[i].R;j++) pos[j]=i;
f[i].build();
}
while (m--)
{
char c=getc();
if (c=='A')
{
int l=read(),r=read(),x=read();
if (pos[l]==pos[r])
{
for (int i=l;i<=r;i++) a[i]+=x;
f[pos[l]].build();
}
else
{
for (int i=pos[l]+;i<pos[r];i++) f[i].add(x);
for (int i=l;i<=f[pos[l]].R;i++) a[i]+=x;f[pos[l]].build();
for (int i=f[pos[r]].L;i<=r;i++) a[i]+=x;f[pos[r]].build();
}
}
else
{
int l=read(),r=read();
if (pos[l]==pos[r])
{
for (int i=l;i<=r;i++) tmp[i]=a[i]+f[pos[l]].lazy;
printf(LL,getseq(l,r));
}
else
{
for (int i=l;i<=f[pos[l]].R;i++) tmp[i]=a[i]+f[pos[l]].lazy;
for (int i=f[pos[r]].L;i<=r;i++) tmp[i]=a[i]+f[pos[r]].lazy;
ll ans=max(getseq(l,f[pos[l]].R),getseq(f[pos[r]].L,r)),s;
for (int i=pos[l]+;i<pos[r];i++) ans=max(ans,f[i].maxseq());
for (int i=pos[l];i<=pos[r];i++) PRE[i]=SUF[i]=;
s=;for (int i=f[pos[l]].R;i>=l;i--) s+=tmp[i],SUF[pos[l]]=max(SUF[pos[l]],s);
s=;for (int i=f[pos[r]].L;i<=r;i++) s+=tmp[i],PRE[pos[r]]=max(PRE[pos[r]],s);
for (int i=pos[l]+;i<pos[r];i++) SUF[i]=max(f[i].maxsuf(),SUF[i-]+f[i].sum);
for (int i=pos[r]-;i>pos[l];i--) PRE[i]=max(f[i].maxpre(),PRE[i+]+f[i].sum);
for (int i=pos[l];i<pos[r];i++) ans=max(ans,SUF[i]+PRE[i+]);
printf(LL,ans);
}
}
}
return ;
}
BZOJ5089 最大连续子段和(分块)的更多相关文章
- bzoj5089 最大连续子段和 分块+复杂度分析+凸包
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5089 题解 本来打算迟一点再写这个题解的,还有一个小问题没有弄清楚. 不过先写一下存个档吧. ...
- 【bzoj5089】最大连续子段和 分块+单调栈维护凸包
题目描述 给出一个长度为 n 的序列,要求支持如下两种操作: A l r x :将 [l,r] 区间内的所有数加上 x : Q l r : 询问 [l,r] 区间的最大连续子段和. 其中,一 ...
- BZOJ5089: 最大连续子段和
维护一个序列支持以下操作:区间加,区间求最大子段和.n<=50000,m<=50000. 我TM再也不写分块了... 先分块,对于块整体加的操作,假设块里面有若干二元组(x,y),表示一个 ...
- HDOJ-1003 Max Sum(最大连续子段 动态规划)
http://acm.hdu.edu.cn/showproblem.php?pid=1003 给出一个包含n个数字的序列{a1,a2,..,ai,..,an},-1000<=ai<=100 ...
- HDU 1003:Max Sum(DP,连续子段和)
Max Sum Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Su ...
- HPU 1007: 严格递增连续子段(贪心)
1007: 严格递增连续子段 [模拟] 时间限制: 1 Sec 内存限制: 128 MB提交: 244 解决: 18 统计 题目描述 给定一个有NN个正整数组成的序列,你最多可以改变其中一个元素,可以 ...
- HDU 1003 最大连续子段和
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1003 Max Sum Time Limit: 2000/1000 MS (Java/Others)M ...
- [题解](线段树最大连续子段和)POJ_3667_Hotel
题意:1.求一个最靠左的长x的区间全部为0,并修改为1,输出这个区间的左端点 2.修改一个区间为0 实际上是维护最大连续子段和,原来也写过 大概需要维护一个左/右最大子段和,当前这段最大子段长,再维护 ...
- JDOJ 2982: 最大连续子段和问题
洛谷 P1115 最大子段和 洛谷传送门 JDOJ 2982: 最大连续子段和问题 JDOJ传送门 题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入格式 第一行是一个正整数NN, ...
随机推荐
- 我在华为,软件测试人员在工作中如何运用Linux?
从事过软件测试的小伙们就会明白会使用Linux是多么重要的一件事,工作时需要用到,面试时会被问到,简历中需要写到.对于软件测试人员来说,不需要你多么熟练使用Linux所有命令,也不需要你对Linux系 ...
- Unity扩展编辑器二
Unity支持自行创建窗口,也支持自定义窗口布局,在Project视图中创建一个Editor文件夹,在文件夹中创建一条脚本 自定义窗口需要让脚本继承EditorWindow在设置MenuItem,此时 ...
- MapReduce 基础学习
什么是MapReduce? mapreduce 是一种软件框架 mapreduce job将任务分解为独立的块儿到不同的map task,进行并行处理: map任务输出会做相应的排序处理,并作为r ...
- js数组知识点总结及经典笔试题
1.判断数组 这是笔试里经常会出现的知识考察点,总结一下 (1)Array.isArray()方法判断 var a=[]; Array.isArray(a) //返回true var b='hello ...
- (2) English Learning
数词 数词有基数词和序数词两种.英语的数词可以作句子的主语.宾语.表语和定语. 基数词:表示数目的词叫基数词. 1. 英语中常用的基数词有:除了图片上的,还有以下一些 1000→one(a) th ...
- Vue.js 相关知识(基础)
1. Vue.js 介绍 Vue,读音 /vjuː/,类似于 view),是一套用于构建用户界面的渐进式框架(重点在于视图层). 作者:尤雨溪 注:学习 vue.js 时,一定要抛弃 jQuery 的 ...
- tty命令详解
基础命令学习目录首页 原文链接:http://blog.chinaunix.net/uid-9525959-id-2001836.html [功能] 打印连接到标准输入的终端的文件名. [描述] ...
- js循环复制一个div
<html> <head> <title>Test of cloneNode Method</title> <script type=" ...
- Daily Scrum (2015/11/1)
今天晚上我们照例召开了每周末的小组例会,主要总结本周的工作和讨论下一周的工作. 首先是本周的一些主要工作: 1.进行了代码的修改和完善. 2.开始进行服务器配置和UI. 3.学习借鉴了nutch爬虫的 ...
- OO第一阶段作业总结
对于OO这门课,学长学姐偶尔提起,大家都略有耳闻,但是并没有将其和计组相提并论.因此,在刚开始接触的时候,并不认为其会比计组难到哪里去,然而事实证明,还是不要想当然去判断,以及不提前学好JAVA对于O ...