[未知OJ]山海經 題解 線段樹
阿新 • • 發佈:2020-07-26
題意:給出一個數列a,每次詢問區間[l..r]的最大連續子段和,以及這個連續子段的兩個端點。如果有多組解,則輸出左端點最小的,如果仍有多組解,則輸出右端點最小的解。
我也不知道這題是哪個OJ的。。但是這題有個簡單版
這題毒瘤就毒瘤在求這兩個端點。對於線段樹的每個節點,除了記錄區間和,字首最大和,字尾最大和,最大連續子段和,還要分別記錄這幾種和的斷點。說起來挺容易,但是一到程式碼實現上,就變得非常詭異了。
#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; 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; }