线段树 洛谷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); }