1. 程式人生 > >POJ3468 A Simple Problem with Integers(區間更新)

POJ3468 A Simple Problem with Integers(區間更新)

A Simple Problem with Integers
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 106587 Accepted: 33254
Case Time Limit: 2000MS

Description

You have N integers, A1A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa

Aa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

       線段樹的更新分為區間更新和單點更新,單點更新容易理解,這裡給大家詳細說明一下區間更新。
       所謂區間更新,我們對一整段的數進行更新操作時,如果繼續單點更新會非常複雜,但是我們對此區間的數修改一下就能得到 我們想要的結果。牽一髮動全身,我們對該區間修改完成後,對子節點也要進行修改,我們並不立刻執行這個操作,否則就和單點操作一樣了,我們將這個向下的操作pushdown放在查詢操作中,並且用lazy陣列標記,lazy標記的是該區間的所有子節點的區間都要修改,這就是延遲操作。
更新操作

void update(int l,int r,int rt,int ll,int rr,int x){
    if(ll<=l&&rr>=r){//當前的區間包含在要修改的區間範圍內時,進行修改
        t[rt] += (r-l+1)*x;
        lazy[rt] += x;//該區間被標記
        return;
    }
    pushdown(rt,r-l+1);//向下進行標記
    int mid = (l+r)>>1;
    if(ll<=mid)
        update(lson,ll,rr,x);
    if(rr>mid)
        update(rson,ll,rr,x);
    t[rt] = t[rt<<1]+t[rt<<1|1];
}
       在這裡可能會有疑問,如果一個區間被連續修改好幾次,我們怎麼記錄好幾次的修改過程呢?其實我們用lazy陣列就能解決這個問題,我們在進行pushdown操作的時候,將這個標記向下進行延伸,lazy陣列是最終儲存的是對該區間所有操作後的最終結果。
       延遲標記

void pushdown(int rt,int len){
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        t[rt<<1] += lazy[rt]*(len-(len>>1));
        t[rt<<1|1] += lazy[rt]*(len>>1);
        lazy[rt]=0;
    }
}
       我們在查詢操作的時候進行更多的pushdown操作,每次查詢到的區間如果不是我們想要的,但是這個區間被標記過,那麼順手牽羊,就將這個區間一起更改,如果是我們想要的區間,我們將此區間的結果返回即可。
long long int query(int l,int r,int rt,int ll,int rr){
    if(ll<=l&&rr>=r){
        return t[rt];
    }
    pushdown(rt,r-l+1);
    int mid = (l+r)>>1;
    long long int ans = 0;
    if(ll<=mid)
        ans+=query(lson,ll,rr);
    if(rr>mid)
        ans+=query(rson,ll,rr);
    return ans;
}
        這就是區間更新,區間更新的最重要一點就是延遲操作,去掉延遲操作,區間更新和單點更新大同小異。下面附上完整程式碼
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
long long int t[100010<<2];
long long int lazy[100010<<2];//延遲標記陣列 
long long int a[100010];
int n,m;
void b(int l,int r,int rt){
    if(l==r){
        t[rt] = a[l];
        return;
    }
    int mid = (l+r)>>1;
    b(lson);
    b(rson);
    t[rt] = t[rt<<1]+t[rt<<1|1];
}
void pushdown(int rt,int len){
    if(lazy[rt]){
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        t[rt<<1] += lazy[rt]*(len-(len>>1));
        t[rt<<1|1] += lazy[rt]*(len>>1);
        lazy[rt]=0;
    }
}
void update(int l,int r,int rt,int ll,int rr,int x){
    if(ll<=l&&rr>=r){
        t[rt] += (r-l+1)*x;
        lazy[rt] += x;
        return;
    }
    pushdown(rt,r-l+1);
    int mid = (l+r)>>1;
    if(ll<=mid)
        update(lson,ll,rr,x);
    if(rr>mid)
        update(rson,ll,rr,x);
    t[rt] = t[rt<<1]+t[rt<<1|1];
}
long long int query(int l,int r,int rt,int ll,int rr){
    if(ll<=l&&rr>=r){
        return t[rt];
    }
    pushdown(rt,r-l+1);
    int mid = (l+r)>>1;
    long long int ans = 0;
    if(ll<=mid)
        ans+=query(lson,ll,rr);
    if(rr>mid)
        ans+=query(rson,ll,rr);
    return ans;
}
int main()
{
    cin>>n>>m;
    char c[2];
    for(int i = 1;i <= n;i++)
        scanf("%I64d",&a[i]);
    b(1,n,1);
    while(m--){
        int ll,rr,x;
        scanf("%s%d%d",c,&ll,&rr);
        if(c[0]=='C'){
            scanf("%d",&x);
            update(1,n,1,ll,rr,x);
        }
        else {
            printf("%I64d\n",query(1,n,1,ll,rr));
        }
    }
    return 0;
}