poj_4047Garden区间更新_求和

来源:互联网 发布:大数据与社会调查 编辑:程序博客网 时间:2024/06/07 01:53



/*  题意:给出一个N个数的序列以及一个k(0<k<=n<=200000),m个操作p,x,y,其中p=0:将x位置的数替换为yp=1:将x y位置的数互换p=2:  查询x-y位置区间连续k个数的和的最大值    解析:求连续区间和最大,可以讲每个区间(我们取左端点)当做一个点,点的附加信息(线段树结子叶点的值)就等于该区间的连续k个数最大值,可以分为1~k,2~k+1...n-k+1~n,一共从1到n-k+1个点构成线段树。线段树中除了子叶结点,其他结点都保存的是子叶结点中的最大值。当x位置值改变时,影响的范围为:    注:我们都是以区间的左端点存入线段树;    (1)假设x位置是某区间的右端点,则其左端点为x-k+1,但是最小端点是从1开始,x-k+1可能小于1,因此要取这两者最大值;    (2)假设x位置是某区间的左端点,再次说明下,我们是以区间的左端点代表该区间存入线段树,因此当x为左端点时要与最大左端点n-k+1比较,取最小值;    求出x位置影响范围后,更新区间[max(1,x-k+1),min(x,n-k+1)],找到更新结点后,回溯向上更新即Getmax函数,当p==1时,区间最值变为_max=_max-v[x]+y;当p==2时,相当于两次p==1的操作,_max=_max-v[x]+v[y],_max=_max-v[y]+v]x];每次某位置值改变后,要在原数组中同时更改    最后查询x到y位置区间最值时,我们是以区间左端点代表区间,在线段树中只需查询l=x,r=y-k+1即可*/#include <cstdio>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;#define INF 0x3f3f3f3f#define inf -0x3f3f3f3f#define mem0(a) memset(a,0,sizeof(a))const int maxn = 200000+10;int sum;int v[maxn],b[maxn];struct node{    int l,r,_max,lazy;}a[maxn<<2];void Getmax(int cur){    a[cur]._max = max(a[cur<<1]._max,a[cur<<1|1]._max);}void build(int l,int r,int cur){    a[cur].l = l;    a[cur].r = r;    a[cur].lazy = 0;    int mid = (l + r )>>1;    if(l == r){        a[cur]._max = b[l];        return ;    }    build(l,mid,cur<<1);    build(mid+1,r,cur<<1|1);    Getmax(cur);}void pushdown(int cur){    if(a[cur].lazy){        //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递         //所以是 “+=”         a[cur<<1].lazy += a[cur].lazy;        a[cur<<1|1].lazy += a[cur].lazy;        //根据标志域设置孩子节点的值。因为我们是求区间最大值,因此当区间内每个元         //素加上一个值时,区间的最大值也加上这个值        a[cur<<1]._max += a[cur].lazy;        a[cur<<1|1]._max += a[cur].lazy;        a[cur].lazy = 0;//清除标记    }}void update(int l,int r,int v,int cur){    //当前节点区间包含在更新区间内    if( l <= a[cur].l &&  r >= a[cur].r){        a[cur]._max += v;        a[cur].lazy += v;        return ;    }    pushdown(cur); //延迟标记向下传递    //更新左右孩子节点    int mid = (a[cur].l + a[cur].r )>>1;    if(r <= mid)        update(l,r,v,cur<<1);    else if(l > mid)        update(l,r,v,cur<<1|1);    else {        update(l,mid,v,cur<<1);        update(mid+1,r,v,cur<<1|1);    }    Getmax(cur);//根据左右子树的值回溯更新当前节点的值}int query(int l,int r,int cur){    if( l <= a[cur].l   && r >= a[cur].r){        return a[cur]._max;    }//若等于要求的区间,则返回结点保存的最值    pushdown(cur);//----延迟标志域向下传递    int mid = (a[cur].l + a[cur].r )>>1;    //分别从左右子树查询,返回两者查询结果的较大值    if(r <= mid )        return query(l,r,cur<<1);    else if(l > mid)        return query(l,r,cur<<1|1);    else {        return max(query(l,mid,cur<<1),query(mid+1,r,cur<<1|1));    }}int main(){    int t;    scanf("%d",&t);    while(t--){        int n,m,k;        scanf("%d%d%d",&n,&m,&k);        for(int i = 1 ;i <= n ; i++)            scanf("%d",&v[i]);        mem0(b);        for(int i = 1 ; i <= k ; i++)            b[1]+=v[i];//第一段和,从1~k        for(int i = 2 ; i <= n-k+1;i++)            b[i]=b[i-1]-v[i-1]+v[i+k-1];        //求剩下每k段和,2~k+1...n-k+1~n        build(1,n-k+1,1);        while(m--){            int c,x,y;            scanf("%d%d%d",&c,&x,&y);            if(c==0){                update(max(1,x-k+1),min(n-k+1,x),y-v[x],1);                v[x]=y;            }            else if(c== 1){                update(max(1,x-k+1),min(n-k+1,x),v[y]-v[x],1);                update(max(1,y-k+1),min(n-k+1,y),v[x]-v[y],1);                swap(v[x],v[y]);            }            else                printf("%d\n",query(x,y-k+1,1));        }    }    return 0;}


0 0