线段树 洛谷P3372

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

 

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

 

输出格式:

 

输出包含若干行整数,即为所有操作2的结果。

 

输入输出样例

输入样例#1:
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1:
11
8
20

 

转载一篇不错的线段树文章:http://blog.csdn.net/zearot/article/details/48299459

进行区间更新、区间查询,时间复杂度都是 O(logN) 的一种数据结构。

代码:

#include <iostream>
#include <cstring>
#define LL long long 
using namespace std;

const int MAX = 100005;

int n, m;
LL a[MAX];
LL tree[4*MAX];        //每个结点(区间)对应的和是多少
LL mark[4*MAX];        //懒标记 

int buildTree(int root, int l, int r);        //建树 
LL add(int root, int l, int r, int L, int R, LL num);        //区间 [L, R] 中的每个数增加 num。当前结点所对应的区间为 [l, r]。返回值为该区间和一共增加了多少。 
LL query(int root, int l, int r, int L, int R);                //返回区间 [L, R] 的和是多少。当前区间为 [l, r] 

int main(){
//    freopen("input.txt", "r", stdin);
    
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++){
        scanf("%lld", &a[i]);
    }
    
    buildTree(1, 1, n);
    for(int i=1; i<=m; i++){
        int opt, x, y;
        LL z;
        scanf("%d%d%d", &opt, &x, &y);
        if(opt == 1){
            scanf("%lld", &z);
            add(1, 1, n, x, y, z);
        }else{
            printf("%lld\n", query(1, 1, n, x, y));
        }
    }
    
    return 0;
}

int buildTree(int root, int l, int r){
    mark[root] = 0;
    if(l == r){
        tree[root] = a[l];
        return a[l];
    }
    int mid = (l + r) / 2;
    tree[root] = buildTree(2*root, l, mid) + buildTree(2*root+1, mid+1, r);
    return tree[root];
}

LL add(int root, int l, int r, int L, int R, LL num){
    if(l > R || r < L || l > r){        //不可能有交集 
        return 0;
    }
    if(l >= L && r <= R){                    //当前区间是要求的区间的一部分 
        mark[root] += num * (r - l + 1);    //进行懒标记,当前区间的和要增加这么多(当前区间的子区间在查询时,如果有需要再进行更新) 
        return num * (r - l + 1);
    }
    int mid = (l + r) / 2;
    LL addNum1 = add(2*root, l, mid, L, R, num);
    LL addNum2 = add(2*root+1, mid+1, r, L, R, num);    //左、右区间一共增加了多少
    tree[root] += addNum1 + addNum2;
    return addNum1+addNum2; 
}

LL query(int root, int l, int r, int L, int R){
    if(l > R || r < L || l > r){        //不可能有交集 
        return 0;
    }
    if(l >= L && r <= R){            //为要求区间的一部分 
        return tree[root] + mark[root];
    }
    int mid = (l + r) / 2;
    if(mark[root] != 0){            //要将懒标记清零了,并且向下推 
        LL num = mark[root] / (r - l + 1);
        mark[2*root] += num * (mid - l + 1);
        mark[2*root+1] += num * (r - mid);
        tree[root] += mark[root];
        mark[root] = 0;
    }
    return query(2*root, l, mid, L, R) + query(2*root+1, mid+1, r, L, R);
}

 

posted @ 2017-08-28 20:35  淡蓝色光  阅读(134)  评论(0编辑  收藏  举报