题意:给定一个环形序列,可以进行在线操作,每次修改一个其中元素。每次修改之后立即输出环上的最大连续子序列的和,要求子序列的长度不得为n,也即不能是整个环。
思路:比较复杂的线段树(http://blog.csdn.net/non_cease/article/details/7437690)把环从一个地方切断拉成一条直线(最直观当然是从n~1之间切断),用线段树记录当前区间的非空最大子列和当前区间的非空最小子列。如果环上的数都是正整数,答案是:环上数的总和-根结点的非空最小子列;否则,答案是:max{根结点的非空最大子列,环上数的总和-根结点的非空最小子列},每次问答的复杂度是O(logN)。
结点内记录的内容比较多,需要记录序列的从左向右最大连续序列值lmax,从右往左的最大连续序列值rmax,和从左向右最小连续序列值lmin,从右往左的最小连续序列值rmin,和每个序列的最大连续序列值semax和最小连续序列值semin,以及在当前区域的和sum。更新起来也就比较显然。
#include <stdio.h> #include <string.h> #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define N 100005 struct node{ int sum; int semax,semin; int lmax,rmax; int lmin,rmin; }t[N<<2]; int n,m; int mid(int a,int b){ return (a+b)>>1; } void update(int r){ int lson,rson; lson = r<<1; rson = lson+1; t[r].sum = t[lson].sum + t[rson].sum; t[r].semax = max(max(t[lson].semax , t[rson].semax), t[lson].rmax+t[rson].lmax); t[r].semin = min(min(t[lson].semin , t[rson].semin), t[lson].rmin+t[rson].lmin); t[r].lmax = max(t[lson].lmax, t[lson].sum+t[rson].lmax); t[r].lmin = min(t[lson].lmin, t[lson].sum+t[rson].lmin); t[r].rmax = max(t[rson].rmax, t[rson].sum+t[lson].rmax); t[r].rmin = min(t[rson].rmin, t[rson].sum+t[lson].rmin); } void build(int r,int a,int b){ int k = mid(a,b); if(a == b){ scanf("%d",&t[r].sum); t[r].semax = t[r].semin = t[r].lmax = t[r].lmin = t[r].rmax = t[r].rmin = t[r].sum; return; } build(r*2, a, k); build(r*2+1, k+1, b); update(r); } void alter(int x,int y,int r,int a,int b){ int k=mid(a,b); if(a==x && b==x){ t[r].sum = y; t[r].semax = t[r].semin = t[r].lmax = t[r].lmin = t[r].rmax = t[r].rmin = t[r].sum; return; } if(x<=k) alter(x, y, r*2, a, k); else alter(x, y, r*2+1, k+1, b); update(r); } int main(){ int i,x,y; scanf("%d",&n); build(1,1,n); scanf("%d",&m); for(i = 0;i<m;i++){ scanf("%d %d",&x,&y); alter(x,y,1,1,n); if(t[1].sum == t[1].semax) printf("%d\n",t[1].sum-t[1].semin); else printf("%d\n",max(t[1].sum-t[1].semin, t[1].semax)); } return 0; }