题目链接:http://120.77.82.93/senior/#main/show/1537 (如果进得去的话)
Description
Input
第二行,n个数,表示对于每盆花的喜好值。
第三行:m, 改动的次数
以下m行,每行两个数ki 和di 。
Output
Sample Input
5 3 -2 1 2 -5 4 2 -2 5 -5 2 -4 5 -1
Sample Output
4 4 3 5
考试的时候:嘛。。看到这个:从1..n编号(n<=100000) emmm....往下看又看到这个:于是他做了m(m<=100000)个改动 emmmm...好吧。因为要单点修改,数据量本身又比较大,所以我猜用线段树,在上面dp。又看到这个:它们围成了一个圈 思维就固化了,认为理所应当要把他们拆成 2*n 长度的链。然后想都不用想就炸了,只有20分,还没其他两题打暴力拿的分高。。
考试之后:因为要调整情绪(因为懒) ,就把这题放了一天。对今天讲课的内容,我感到很不适应,就离场来改这题了(摸了)。
如上所言(如林卓铖大佬所言),这题虽然确实要用线段树,但是和dp一点关系都没有(必要数据都在线维护了还要个啥dp啊)。本蒟蒻的做法总共需要维护七个值,区间总和sum、区间最大/小子序列和、区间最大/小前缀和、区间最大/小后缀和。有的人的做法只用维护五个,我觉得那样更优。接下来回顾一下这些值维护来干什么。
拿“最大xxx”那一组做例子。
- 编号k所表示的区间的最大子序列和,可能是“左子区间的最大子序列和”,可能是“右子区间的最大子序列和”,还可能是“左子区间的最大后缀和”加上“右子区间的最大前缀和”。
- 编号k所表示的区间的最大前缀和,可能是“左子区间的最大前缀和”,也可能是“左子区间的总和”加上“右子区间的最大前缀和”。
- 编号k所表示的区间的最大后缀和同上类似。
“最小xxx”类似。
——大概说明白了吧?
然后是逐步插入,维护,插入,维护......
然后是更新,输出,更新,输出......
说说怎么用维护好的数据们得到答案。刚刚有提到“不用拆环”,这是因为在线段树里有“最小xxx”这一组数据存在。想一想,花盆们是个环,现在我们把它们用区间【1-N】表示出来。此时如果正确答案是选中间的【i-j】(1<=i<=j<=N),那么输出区间【1-N】的最大子序列和就好了。因为它是环,所以正确答案还有一种可能,就是选【1-i】和【j-N】两段(就是不选中间的某一段),那,这不就是区间总和减去区间最小子段和?就是这样啦
还有一点可能得注意一下,题目说“且不能整圈都选”。一个处理办法:判断你的程序有没有可能把整圈都选上的情况,如果可能,那一定有:整圈的喜爱值都是正的。这时候特判一下,如果你算出来的“总区间的总和”与“最大子序列和”相等,那就挑一个最小的数减去它,得到的就是正确答案。
附码:
#include#include #include #include using namespace std; int n,m; int a,b; //工 具 数 int sum[1000000]; int maxx[1000000],maxl[1000000],maxr[1000000];//最大子段和、最大前缀和、最大后缀和 int minx[1000000],minl[1000000],minr[1000000];//最小子段和、最小前缀和、最小后缀和 inline int maxxx(int x,int y,int z){//别吐槽本蒟蒻码风啦。我真的不知道有什么函数能比较三个数大小 int c; c=max(x,y); c=max(c,z); return c; } inline int minxx(int x,int y,int z){ int c; c=min(x,y); c=min(c,z); return c; } void add(int k,int l,int r,int p,int v){//维护操作 if(p==l&&p==r){ maxl[k]=maxr[k]=sum[k]=maxx[k]=minx[k]=v; return; } if(l>p||r return; int mid=l+r>>1; add(k*2,l,mid,p,v); add(k*2+1,mid+1,r,p,v); //以下开始维护 sum[k]=sum[k*2]+sum[k*2+1]; maxx[k]=maxxx(maxx[k*2],maxx[k*2+1],maxr[k*2]+maxl[k*2+1]); minx[k]=minxx(minx[k*2],minx[k*2+1],minr[k*2]+minl[k*2+1]); maxl[k]=max(maxl[k*2],sum[k*2]+maxl[k*2+1]); minl[k]=min(minl[k*2],sum[k*2]+minl[k*2+1]); maxr[k]=max(maxr[k*2+1],sum[k*2+1]+maxr[k*2]); minr[k]=min(minr[k*2+1],sum[k*2+1]+minr[k*2]); //维护这七个值 } int main(){ cin>>n; for(int i=1; i<=n; i++) { scanf("%d", &a); add(1, 1, n, i, a); } cin>>m; while(m--) { scanf("%d%d", &a, &b); add(1, 1, n, a, b); int ans; if(sum[1]==maxx[1])ans=sum[1]-minx[1]; //特判 else ans=max(maxx[1],sum[1]-minx[1]); printf("%d\n",ans); } }