P5693 EI 的第六分块 题解
第二分块怎么就比第六分块简单了 第六分块明明好懂好写 我为第六分块正名!
第六分块不用分块。
本篇题解大部分内容跟我的P6792 [SNOI2020] 区间和 题解存在重复,因为我是先写的那道题然后来板题拿的双倍经验。
KTT 维护区间最大子段和板题,本篇题解不含复杂度证明。
学习过程中参考了楼上 dalao 的题解以及EI的博客。
求区间最大子段和,先考虑在线段树上的暴力做法。
朴素的线段树做法是维护四个信息:
怎么维护呢?有以下式子:
-
-
-
-
($ls,rs$ 分别为左右儿子)。
解释一下 考虑最大子段和一共有三种情况:
- 子段全部位于左区间内 即
totmx_{ls} 。 - 子段全部位于右区间内 即
totmx_{rs} 。 - 子段横跨两个区间 这时候左右区间都要做贡献 因为要保证子段连续 所以是
rmx_{1s} + lmx_{rs} 。
求
对于
- 要么直接继承左区间的答案 即
lmx_{ls} 。 - 要么考虑更新 那就是左区间和加上右区间前缀 即
sum_{ls} + lmx_{rs} 。
-
- 否则 阈值就是两条直线交点的横坐标。
这部分的代码:
pair<line,long long> max(line a,line b){
if(a.k<b.k || (a.k==b.k && a.b<b.b)) swap(a,b);
if(a.b>=b.b) return mkp(a,inf);
else return mkp(b,(b.b-a.b)/(a.k-b.k));
}
易知
这部分代码:
node operator + (const node &a) const{
node res;
pair<line,long long> tmp;
res.x=min(x,a.x);
//sum=ls.sum+rs.sum
res.sum=sum+a.sum;
//lmax=max(ls.lmax,ls.sum+rs.lmax)
tmp=max(lmx,sum+a.lmx);
res.lmx=tmp.first;
res.x=min(res.x,tmp.second);
//rmax=max(rs.rmax,rs.sum+ls.rmax)
tmp=max(a.rmx,a.sum+rmx);
res.rmx=tmp.first;
res.x=min(res.x,tmp.second);
//totmax=max(ls.totmax,rs.totmax,ls.rmax+rs.lmax)
tmp=max(totmx,a.totmx);
res.x=min(res.x,tmp.second);
tmp=max(tmp.first,rmx+a.lmx);
res.totmx=tmp.first;
res.x=min(res.x,tmp.second);
return res;
}
具体的复杂度证明去 EI 大佬的博客看吧,蒟蒻不会分析势能 QwQ
代码直接用P6792 [SNOI2020] 区间和的代码改了改() 一些细节问题放注释里啦!
#include<bits/stdc++.h>
#define MAXN 400005
#define inf 1e18
#define ls k<<1
#define rs k<<1|1
#define mkp make_pair
using namespace std;
inline long long read(){
long long x=0;
int f=1;
char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
int n,q;
long long A[MAXN];
//一次函数
struct line{
long long k,b;//b可以认为就是正常的值 跟原来不加一次函数优化是一样的 k即最大子段和这个子段的长度
line operator + (const line &a) const{
return (line){k+a.k,b+a.b};
}
void add(long long v){
b+=k*v;
}
};
pair<line,long long> max(line a,line b){
if(a.k<b.k || (a.k==b.k && a.b<b.b)) swap(a,b);
if(a.b>=b.b) return mkp(a,inf);
else return mkp(b,(b.b-a.b)/(a.k-b.k));
}
struct node{
int l,r;
line lmx,rmx,sum,totmx;
long long x;//阈值
long long tag;
node operator + (const node &a) const{//维护最大子段和需要的四个变量 同时维护阈值
node res;
pair<line,long long> tmp;
res.x=min(x,a.x);
//sum=ls.sum+rs.sum
res.sum=sum+a.sum;
//lmax=max(ls.lmax,ls.sum+rs.lmax)
tmp=max(lmx,sum+a.lmx);
res.lmx=tmp.first;
res.x=min(res.x,tmp.second);
//rmax=max(rs.rmax,rs.sum+ls.rmax)
tmp=max(a.rmx,a.sum+rmx);
res.rmx=tmp.first;
res.x=min(res.x,tmp.second);
//totmax=max(ls.totmax,rs.totmax,ls.rmax+rs.lmax)
tmp=max(totmx,a.totmx);
res.x=min(res.x,tmp.second);
tmp=max(tmp.first,rmx+a.lmx);
res.totmx=tmp.first;
res.x=min(res.x,tmp.second);
return res;
}
}t[MAXN<<2];
void pushup(int k){
node tmp=t[ls]+t[rs];//重载的运算符只维护了KTT 别把除KTT之外的正常线段树信息搞没了
tmp.tag=t[k].tag;
tmp.l=t[k].l;
tmp.r=t[k].r;
t[k]=tmp;
}
void build(int k,int l,int r){
t[k].l=l;t[k].r=r;
if(l==r){
line tmp={1,A[l]};//初始值最大子段就是本身 子段长度为1
t[k].lmx=t[k].rmx=t[k].sum=t[k].totmx=tmp;
t[k].x=inf;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(k);
}
void calc(int k,long long v){
t[k].tag+=v;
t[k].sum.add(v);
t[k].totmx.add(v);
t[k].lmx.add(v);
t[k].rmx.add(v);
t[k].x-=v;//别忘了维护阈值
}
void pushdown(int k){
if(t[k].tag){
calc(ls,t[k].tag);
calc(rs,t[k].tag);
t[k].tag=0;
}
}
void upd(int k,long long v){
if(v>t[k].x){//超过阈值的递归重构子树
v+=t[k].tag;
t[k].tag=0;
upd(ls,v);
upd(rs,v);
pushup(k);
}else{
calc(k,v);//没超过阈值就是平凡的区间加
}
}
void update(int k,int l,int r,long long v){
if(t[k].l>=l && t[k].r<=r){
upd(k,v);
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(mid>=l){
update(ls,l,r,v);
}
if(mid<r){
update(rs,l,r,v);
}
pushup(k);
}
node query(int k,int l,int r){
if(t[k].l>=l && t[k].r<=r){
return t[k];
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(mid>=r) return query(ls,l,r);
if(mid<l) return query(rs,l,r);
return query(ls,l,mid)+query(rs,mid+1,r);
}
int main(){
n=read();q=read();
for(int i=1;i<=n;i++){
A[i]=read();
}
build(1,1,n);
int op,l,r;
long long x;
while(q--){
op=read();
l=read();r=read();
if(op==1){
x=read();
update(1,l,r,x);
}else{
long long ans=query(1,l,r).totmx.b;
printf("%lld\n",max(0ll,ans));
}
}
return 0;
}