[未知OJ]山海经 题解 线段树
题意:给出一个数列a(n<=1e5),每次询问区间[l..r]的最大连续子段和,以及这个连续子段的两个端点。如果有多组解,则输出左端点最小的,如果仍有多组解,则输出右端点最小的解。
此题有简单版
这题毒瘤就毒瘤在求这两个端点。对于线段树的每个节点,除了记录区间和,前缀最大和,后缀最大和,最大连续子段和,还要分别记录前缀最大和,后缀最大和,最大连续子段和的端点,似乎这题码量算少的?
#include<bits/stdc++.h>
using namespace std;
#define scanf a1234=scanf
int a1234;
const int mxn=1e5+3;int n,T,a[mxn];
struct tourist{
int lnum,rnum,sum,num,ll,rr,ln,rn;
//ll是lnum的端点 rr是rnum的端点 ln rn是num的端点
inline void pr(int l,int r){
printf("%d %d %d %d %d %d %d %d %d %d\n",l,r,lnum,rnum,sum,num,ll,rr,ln,rn);
}
};
#define mid ((l+r)>>1)
class segm{
public:
tourist tr[mxn*4];
inline void Build(int x,int l,int r){
if(l==r)return tr[x]={a[l],a[l],a[l],a[l],l,l,l,l},void();
Build(x*2,l,mid),Build(x*2+1,mid+1,r);
up(x);
}
inline void up(int x){
tr[x]=merge(tr[x*2],tr[x*2+1]);
}
inline tourist merge(tourist &a,tourist &b){
static tourist res;
res.sum=a.sum+b.sum;
int aa=a.lnum,bb=a.sum+b.lnum;
if(aa>=bb)res.lnum=aa,res.ll=a.ll;else res.lnum=bb,res.ll=b.ll;
aa=b.rnum,bb=b.sum+a.rnum;
if(bb>=aa)res.rnum=bb,res.rr=a.rr;else res.rnum=aa,res.rr=b.rr;
aa=a.num,bb=b.num;
if(aa>=bb)res.num=aa,res.ln=a.ln,res.rn=a.rn;
else res.num=bb,res.ln=b.ln,res.rn=b.rn;
aa=a.rnum+b.lnum; int ll=a.rr,rr=b.ll;
if(aa>res.num|| (aa==res.num&&ll<res.ln) /*|| (aa==res.num&&ll==res.ln&&rr<res.rn)*/)res.num=aa,res.ln=ll,res.rn=rr;
return res;
}
inline tourist qask(int x,int l,int r,int lc,int rc){
if(lc<=l&&r<=rc)return tr[x];
if(lc>mid)return qask(x*2+1,mid+1,r,lc,rc);
if(rc<=mid)return qask(x*2,l,mid,lc,rc);
tourist lres=qask(x*2,l,mid,lc,rc),rres=qask(x*2+1,mid+1,r,lc,rc);
return merge(lres,rres);
}
}seg;
#undef mid
int main(){
scanf("%d%d",&n,&T);for(int i=1;i<=n;++i)scanf("%d",a+i);
seg.Build(1,1,n);
while(T--){
int l,r;scanf("%d%d",&l,&r);
static tourist tql; tql=seg.qask(1,1,n,l,r);
printf("%d %d %d\n",tql.ln,tql.rn,tql.num);
}
return 0;
}