POJ 3468 【區間修改+區間查詢 樹狀陣列 | 線段樹 | 分塊】

神探小小迪發表於2018-09-27

You have N integers, A1, A2, ... , 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 A1, A2, ... , 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 AaAa+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.

題解1:P200.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int maxn = 100000+7;
int n, q, a[maxn];
ll sum[maxn], c[2][maxn];
ll ans = 0;
int lowbit(int x){
    return x & -x;
}
ll ask(int k, int x){
    ll sum = 0;
    while(x){
        sum += c[k][x];
        x -= lowbit(x);
    }
    return sum;
}
void add(int k, int x, int y){
    while(x <= n){
        c[k][x] += y;
        x += lowbit(x);
    }
}
int main()
{
    scanf("%d %d", &n, &q);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        sum[i] = sum[i-1] + a[i];
    }
    int l, r, w;
    while(q--){
        char ch[2];
        scanf("%s", ch);
        if(ch[0] == 'Q'){
            ans = 0;
            scanf("%d %d", &l, &r);
            ans += (sum[r] + (r+1)*ask(0, r) - ask(1, r));
            ans -= (sum[l-1] + l*ask(0, l-1) - ask(1, l-1));
            printf("%lld\n", ans);
        }else{
            scanf("%d %d %d", &l, &r, &w);
            add(0, l, w);
            add(0, r+1, -w);
            add(1, l, l*w);
            add(1, r+1, -(r+1)*w);
        }
    }
    return 0;
}

方法2:線段樹+延遲標記,P210

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define INF -0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 100010;
int n, m;
int a[maxn];
struct SegmentTree{
    int l, r;
    ll sum, add;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define add(x) t[x].add
}t[maxn<<2];
void push_up(int p){
    sum(p) = sum(2*p) + sum(2*p+1);
}
void build(int p, int l, int r){
    l(p) = l, r(p) = r;
    if(l == r){
        sum(p) = a[l];
        return ;
    }
    int mid = (l+r)/2;
    build(2*p, l, mid);
    build(2*p+1, mid+1, r);
    push_up(p);
}
void push_down(int p){
    if(add(p)){//節點p有標記
        sum(2*p) += add(p)*(r(2*p)-l(2*p)+1);//更新左子節點資訊
        sum(2*p+1) += add(p)*(r(2*p+1)-l(2*p+1)+1);//更新右子節點資訊
        add(2*p) += add(p);//給左子節點打延遲標記
        add(2*p+1) += add(p);//給右子節點打延遲標記
        add(p) = 0;//清除p的標記
    }
}
void change(int p, int l, int r, int d){
    if(l <= l(p) && r >= r(p)){//完全覆蓋
        sum(p) += d*(r(p)-l(p)+1);//更新節點資訊
        add(p) += d;//給節點打延遲標記
        return ;
    }
    push_down(p);//下傳延遲標記
    int mid = (l(p)+r(p)) / 2;
    if(l <= mid) change(2*p, l, r, d);
    if(r > mid) change(2*p+1, l, r, d);
    push_up(p);
}
ll ask(int p, int l, int r){
    if(l <= l(p) && r >= r(p)){
        return sum(p);
    }
    push_down(p);//下傳延遲標記
    int mid = (l(p)+r(p)) / 2;
    ll ans = 0;
    if(l <= mid) ans += ask(2*p, l, r);
    if(r > mid) ans += ask(2*p+1, l, r);
    return ans;
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }
    build(1,1,n);
    while(m--){
        char op[2];
        int l, r, d;
        scanf("%s%d%d", op, &l, &r);
        if(op[0]=='C'){
            scanf("%d", &d);
            change(1,l,r,d);
        }else{
            ll ans = ask(1, l, r);
            printf("%lld\n", ans);
        }
    }
    return 0;
}

3、分塊,P215

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 100010;
int n, m, t;
ll sum[maxn], a[maxn], add[maxn];
ll L[maxn], R[maxn];
int pos[maxn];
void change(int l, int r, ll v){
    int p = pos[l], q = pos[r];
    if(p == q){
        for(int i = l; i <= r; i++) a[i] += v;
        sum[p] += (r-l+1)*v;
    }else{
        for(int i = p+1; i <= q-1; i++)
            add[i] += v;
        for(int i = l; i <= R[p]; i++)
            a[i] += v;
        sum[p] += (R[p]-l+1)*v;
        for(int i = L[q]; i <= r; i++)
            a[i] += v;
        sum[q] += (r-L[q]+1)*v;
    }
}
ll ask(int l, int r){
    ll ans = 0;
    int p = pos[l], q = pos[r];
    if(p == q){
        for(int i = l; i <= r; i++)
            ans += a[i];
        ans += add[p]*(r-l+1);
    }else{
        for(int i = p+1; i <= q-1; i++)
            ans += sum[i] + add[i]*(R[i]-L[i]+1);
        for(int i = l; i <= R[p]; i++)
            ans += a[i];
        ans += add[p]*(R[p]-l+1);
        for(int i = L[q]; i <= r; i++)
            ans += a[i];
        ans += add[q]*(r-L[q]+1);
    }
    return ans;
}
int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        scanf("%lld", &a[i]);
    }
    t = int(sqrt(n*1.0));
    for(int i = 1; i <= t; i++){
        L[i] = int((i-1)*sqrt(n*1.0)) + 1;
        R[i] = int(i*sqrt(n*1.0));
    }
    if(R[t] < n){
        t++, L[t] = R[t-1] + 1;
        R[t] = n;
    }
    for(int i = 1; i <= t; i++){
        for(int j = L[i]; j <= R[i]; j++){
            pos[j] = i;
            sum[i] += a[j];
        }
    }
    while(m--){
        char op[2];
        int l, r;
        ll d;
        scanf("%s %d %d", op, &l, &r);
        if(op[0] == 'C'){
            scanf("%lld", &d);
            change(l, r, d);
        }else{
            printf("%lld\n", ask(l, r));
        }
    }
    return 0;
}

 

相關文章