【JZOI1537】pot

题目链接:http://120.77.82.93/senior/#main/show/1537  (如果进得去的话)

Description

这个假期,小h在自家院子里种了许多花,它们围成了一个圈,从1..n编号(n<=100000),小h 对每盆花都有一个喜好值xi,(-1000<=xi<=1000),小h现在觉得这样一成不变很枯燥,于是他做了m(m<=100000)个改动,每次把第ki盘花改成喜好值为di的花,然后小h要你告诉他,在这个花圈中,连续的最大喜好值是多少。
 

Input

第一行,n,花盆的数量
第二行,n个数,表示对于每盆花的喜好值。
第三行:m, 改动的次数
以下m行,每行两个数ki 和di 。

Output

M行,每一行对应一个更改,表示连续的最大喜好值,且不能整圈都选。(注意:是在圈上找)
 

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”那一组做例子。

  1. 编号k所表示的区间的最大子序列和,可能是“左子区间的最大子序列和”,可能是“右子区间的最大子序列和”,还可能是“左子区间的最大后缀和”加上“右子区间的最大前缀和”。
  2. 编号k所表示的区间的最大前缀和,可能是“左子区间的最大前缀和”,也可能是“左子区间的总和”加上“右子区间的最大前缀和”。
  3. 编号k所表示的区间的最大后缀和同上类似。

  “最小xxx”类似。

——大概说明白了吧?

  然后是逐步插入,维护,插入,维护......

  然后是更新,输出,更新,输出......

  说说怎么用维护好的数据们得到答案。刚刚有提到“不用拆环”,这是因为在线段树里有“最小xxx”这一组数据存在。想一想,花盆们是个环,现在我们把它们用区间【1-N】表示出来。此时如果正确答案是选中间的【i-j】(1<=i<=j<=N),那么输出区间【1-N】的最大子序列和就好了。因为它是环,所以正确答案还有一种可能,就是选【1-i】和【j-N】两段(就是不选中间的某一段),那,这不就是区间总和减去区间最小子段和?就是这样啦

  还有一点可能得注意一下,题目说“且不能整圈都选”。一个处理办法:判断你的程序有没有可能把整圈都选上的情况,如果可能,那一定有:整圈的喜爱值都是正的。这时候特判一下,如果你算出来的“总区间的总和”与“最大子序列和”相等,那就挑一个最小的数减去它,得到的就是正确答案。

  附码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
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<p)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);
    }
    
}

猜你喜欢

转载自www.cnblogs.com/snowysniper/p/11354252.html