poj_4047Garden區間更新_求和



/*  題意:
給出一個N個數的序列以及一個k(0<k<=n<=200000),m個操作p,x,y,其中
p=0:將x位置的數替換爲y
p=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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章