[BZOJ 1756]小白逛公园
这题是很经典的线段树题。
我们可以发现最长子序列或跨越中点,或不跨越中点。若不跨越则肯定在左子或右子中,跨越则是左子最大后缀和和右子最大前缀和之和。问题便迎刃而解。(当然,求区间最大前缀/后缀和要用上区间和)
需注意此题可能会出现a>b!
代码:
/************************************************************** Problem: 1756 User: danihao123 Language: C++ Result: Accepted Time:2604 ms Memory:49648 kb ****************************************************************/ #include <cstdio> #include <algorithm> using namespace std; const int maxn=500001; struct Node{ int L,R; int maxP,maxS,ans,sum; }; Node Tree[maxn*4]; int A[maxn]; void merge(Node& O,Node& LC,Node& RC){ O.sum=LC.sum+RC.sum; O.maxP=max(LC.maxP,LC.sum+RC.maxP); O.maxS=max(RC.maxS,RC.sum+LC.maxS); O.ans=max(max(LC.ans,RC.ans),LC.maxS+RC.maxP); } void maintain(int o){ Node& O=Tree[o],LC=Tree[o<<1],RC=Tree[o<<1|1]; merge(O,LC,RC); } void build_tree(int o,int L,int R){ Node& O=Tree[o]; O.L=L; O.R=R; if(L==R){ O.maxP=O.maxS=O.ans=O.sum=A[L]; }else{ int M=L+(R-L)/2; build_tree(o<<1,L,M); build_tree(o<<1|1,M+1,R); maintain(o); } } void update(int o,int p,int v){ Node& O=Tree[o]; int& L=O.L,R=O.R; if(L==R){ O.maxP=O.maxS=O.ans=O.sum=v; }else{ int M=L+(R-L)/2; if(p<=M) update(o<<1,p,v); else update(o<<1|1,p,v); maintain(o); } } int ql,qr; Node query(int o){ Node& O=Tree[o]; int& L=O.L,R=O.R; if(ql<=L && R<=qr){ return Tree[o]; } int M=L+(R-L)/2; Node ANS,LC,RC; if(ql<=M){ LC=query(o<<1); if(qr>M){ RC=query(o<<1|1); merge(ANS,LC,RC); return ANS; }else{ return LC; } }else{ RC=query(o<<1|1); return RC; } } int main(){ int n,m; register int i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&A[i]); build_tree(1,1,n); int k,a,b; Node ans; for(i=1;i<=m;i++){ scanf("%d%d%d",&k,&a,&b); if(k&1){ if(a>b) swap(a,b); ql=a; qr=b; ans=query(1); printf("%d\n",ans.ans); }else{ update(1,a,b); } } return 0; }