【Ynoi2013】D2T2
分块,分治。
题目大意:
给定一个长度为 $n$ 的序列,多次询问,每次给出 $l,r,L,R$,需要求出,只保留权值在 $[L,R]$ 中的数,其他数的权值为 $0$ 时,区间 $[l,r]$ 的最大子段和。
题解:
第七分块太难了就先来写一下这个。
结果交了 59 发才过……
有一个非常简单的做法,将值域离散化之后对值域进行莫队,用线段树维护最大子段和。时间复杂度为 $O(n\sqrt m\log n)$。
考虑更优的做法。如果我们有一种时间复杂度 $O(n^2)$ 的做法求出一个长度为 $n$ 的序列的所有不同值域限制下的全局最大子段和,那我们就可以通过分块将这个复杂度降为 $O(n\sqrt n)$。
关于最大子段和的问题,分治是一个常见的思路。由于长度为 $n$ 的序列,只有 $O(n^2)$ 个本质不同的值域限制,所以我们希望每层都能 $O(n^2)$ 合并信息。这样分治的复杂度为 $T(n)=2T(\frac n 2)+O(n^2)$。根据主定理即可得到 $T(n)=O(n^2)$。
我们考虑已知两个子区间的本质不同的值以及限制,如何合并为这一层的答案。对于合并上来的 $[L,R]$,我们需要在左、右分别找到区间 $[L_1,R_1]$ 和 $[L_2,R_2]$,其中 $[L_1,R_1]$ 和 $[L_2,R_2]$ 都是被 $[L,R]$ 包含的最大区间。那么这两个区间的最大子段和信息进行合并,即可得到 $[L,R]$ 的最大子段和信息。
我们考虑预处理对每个 $L$,左、右区间第一个大于等于 $L$ 的值。同样,对于 $R$,预处理左、右区间第一个小于等于 $R$ 的值。由于我们是要选最大的两个区间合并,那么两个端点和 $L,R$ 最接近的一定是最大的。这样就可以直接 $O(n^2)$ 枚举 $L,R$,然后 $O(1)$ 找到两个需要合并的区间,进行合并。
分治的时空复杂度均为 $O(n^2)$,考虑对序列分块,每个块的大小为 $O(\sqrt n)$,则对每个块进行分治的时间复杂度为 $O(n)$。分治花费的时间复杂度为 $O(n\sqrt n)$。
对于一个询问,拆成整块的和边角的。对于边界的显然可以 $O(\sqrt n)$ 解决。对于整块的,我们已经知道所有本质不同的值域限制的答案,那么对于值域限制为 $[L,R]$ 的询问,我们要找到被它包含的最大的区间的信息。那么和上面的处理方式相同,对每个 $L$ 和 $R$ 分别找到最靠近的端点即可。这里为了保证复杂度,必须用双指针处理,否则时间复杂度将退化为 $O(m\sqrt n\log n)$。
由于直接存储所有块的信息需要 $O(n\sqrt n)$ 的空间,无法接受。所以我们对询问离线,在一个块的信息处理完后,直接把这个块对所有询问的贡献都更新掉。这样空间复杂度为 $O(n)$。
时间复杂度为 $O((n+m)\sqrt n)$。
注意实现时的常数。
Code:
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<functional>
using namespace std;
typedef long long LoveLive;
typedef unsigned long long ULL;
#define bel(x)((x-1)/siz+1)
const int N=1e5+5,siz=200,B=siz+2;
char buf[(int)1e7],*ss=buf;
inline int init(){buf[fread(buf,1,(int)1e7-1,stdin)]='\n';fclose(stdin);return 0;}
const int __START__=init();
inline int readint(){
int d=0;bool f=0;
while(!isdigit(*ss))f=*ss++=='-';
while(isdigit(*ss))d=d*10+(*ss++^'0');
return f?-d:d;
}
char bwf[5000005],*ww=bwf;
int xx[24];
inline void print(LoveLive x){
static int*q=xx;
if(!x)*ww++='0';else{
for(;x;x/=10)*q++=(x%10)^'0';
while(q!=xx)*ww++=*--q;
}
*ww++='\n';
}
int n,m,A[N],a[B],beL[N],beR[N],b[B],tmp[B],vec[B];
struct data{
LoveLive lmax,rmax,mx,sum;
inline data(LoveLive a,LoveLive b,LoveLive c,LoveLive d):lmax(a),rmax(b),mx(c),sum(d){}
inline data(){lmax=rmax=mx=sum=0;}
inline data(LoveLive x){sum=x,lmax=rmax=mx=max(x,0LL);}
inline data operator+(const data&rhs)const{
return(data){max(lmax,sum+rhs.lmax),max(rhs.rmax,rhs.sum+rmax),max({mx,rhs.mx,rmax+rhs.lmax}),sum+rhs.sum};
}
inline void operator+=(const data&rhs){mx=max({mx,rhs.mx,rmax+rhs.lmax}),lmax=max(lmax,sum+rhs.lmax),rmax=max(rhs.rmax,rhs.sum+rmax),sum+=rhs.sum;}
}ans[N],p[65536],Lt[65536],Rt[65536];
ULL lf[N],rg[N];
int IT[B];
struct que{
int l,r,L,R;
}q[N];
struct info{
int zt;
data s;
}D[600000],*ed;
void solve(int l,int r,info*V,int&tot){
if(l==r){
V[tot++]=(info){a[l]<<8|a[l],data(vec[a[l]])};
return;
}
const int mid=l+r>>1;
info*L=ed;ed+=(mid-l+1)*(mid-l+2);
info*R=ed;ed+=(r-mid+1)*(r-mid);
int cntL=0,cntR=0;
solve(l,mid,L,cntL),solve(mid+1,r,R,cntR);
int i=l,j=mid+1,t=l,k;
while(i<=mid&&j<=r)if(b[i]<=b[j])tmp[t++]=b[i++];else tmp[t++]=b[j++];
while(i<=mid)tmp[t++]=b[i++];while(j<=r)tmp[t++]=b[j++];
for(int i=0;i<cntL;++i)Lt[L[i].zt]=L[i].s;
for(int i=0;i<cntR;++i)Rt[R[i].zt]=R[i].s;
static int _L[B],_R[B],L_[B],R_[B];
t=l,k=mid+1;
for(int i=l;i<=r;++i){
while(t<=mid&&b[t]<tmp[i])++t;
while(k<=r&&b[k]<tmp[i])++k;
_L[i]=t<=mid?b[t]:114514,L_[i]=k<=r?b[k]:114514;
}
t=mid,k=r;
for(int i=r;i>=l;--i){
while(t>=l&&b[t]>tmp[i])--t;
while(k>mid&&b[k]>tmp[i])--k;
_R[i]=t>=l?b[t]:0,R_[i]=k>mid?b[k]:0;
}
for(int i=l;i<=r;++i)b[i]=tmp[i];
for(int i=l;i<=r;++i)if(i==l||b[i]!=b[i-1])
for(int j=i;j<=r;++j)if(j==i||b[j]!=b[j-1]){
const data&ztL=Lt[_L[i]<<8|_R[j]],&ztR=Rt[L_[i]<<8|R_[j]];const int X=b[i]<<8|b[j];
if(_L[i]<=_R[j]&&L_[i]<=R_[j])V[tot++]=(info){X,ztL+ztR};else
if(_L[i]<=_R[j])V[tot++]=(info){X,ztL};else
if(L_[i]<=R_[j])V[tot++]=(info){X,ztR};
}
ed=L;
}
inline data get(int l,int r,const int&L,const int&R){
LoveLive lm=0,rm=0,sum=0,mx=0,s=0;
for(int i=l;i<=r;++i)if(lm<(s+=(L<=A[i]&&A[i]<=R)?A[i]:0))lm=s;
s=0;
for(int i=r;i>=l;--i)if(rm<(s+=(L<=A[i]&&A[i]<=R)?A[i]:0))rm=s;
s=0;
for(int i=l;i<=r;++i){
const int x=(L<=A[i]&&A[i]<=R)?A[i]:0;
sum+=x,s+=x;
if(mx<s)mx=s;
if(s<0)s=0;
}
return data(lm,rm,mx,sum);
}
int main(){
n=readint(),m=readint();
for(int i=1;i<=n;++i)A[i]=readint();
for(int i=1;i<=m;++i){
int l=readint(),r=readint(),L=readint(),R=readint();
q[i]=(que){l,r,L,R},L+=1000000000,R+=1000000000,lf[i]=(ULL)L<<32|i,rg[i]=(ULL)R<<32|i;
}
sort(lf+1,lf+m+1),sort(rg+1,rg+m+1,greater<ULL>());
for(int l=siz+1,r=2*siz;r<=n;l+=siz,r+=siz){
int len=r-l+1;
for(int i=l;i<=r;++i)vec[i-l]=A[i];
int Val=len;
sort(vec,vec+len);
Val=unique(vec,vec+len)-vec;
for(int i=l;i<=r;++i)a[i-l]=lower_bound(vec,vec+Val,A[i])-vec;
memcpy(b,a,sizeof a);
int tp=0;
ed=D+len*(len+1);
solve(0,len-1,D,tp);
for(int i=0;i<tp;++i)p[D[i].zt]=D[i].s;
int it1=0,it2=Val-1;
for(int i=1;i<=m;++i){
while(it1<Val&&(lf[i]>>32)>vec[it1]+1000000000)++it1;
while(~it2&&(rg[i]>>32)<vec[it2]+1000000000)--it2;
beL[(int)lf[i]]=it1,beR[(int)rg[i]]=it2;
}
for(int i=1;i<=m;++i)if(beL[i]<=beR[i]&&q[i].l<l&&r<q[i].r)ans[i]+=p[beL[i]<<8|beR[i]];
}
for(int i=1;i<=m;++i){
int l=q[i].l,r=q[i].r;
const int bL=bel(l),bR=bel(r);
if(bL==bR)print(get(l,r,q[i].L,q[i].R).mx);else
print((get(l,bL*siz,q[i].L,q[i].R)+ans[i]+get((bR-1)*siz+1,r,q[i].L,q[i].R)).mx);
}
fwrite(bwf,ww-bwf,1,stdout);
return 0;
}