线段树模板1 [Luogu P3372]

时间:2023-03-09 07:59:26
线段树模板1 [Luogu P3372]

代码+注释:

 #include <iostream>
#include <cstdio>
using namespace std; int n, q, flag, x, y, v;
int a[];
struct node{
long long a, b;
}tree[]; //树,a为数据域,b为标记 void build_tree(int now, int l, int r) { //建树
if (l == r) {
tree[now].a = a[l]; //填充数据
return ;
}
int mid = (l + r) >> ; //mid = (l + r) div 2; 位运算加速
build_tree(now + now, l, mid); //往左边继续建树
build_tree(now + now + , mid + , r); //往右边继续建树
tree[now].a = tree[now + now].a + tree[now + now + ].a; //此时节点数据域为下接两个节点的和,便于查询
} void plust(int now, int l, int r, int x, int y) {//区间加操作
if ((l == x) && (r == y) || (l == r)) { //达到要求
tree[now].a += v * (r - l + ); //累加v的值与当前节点下所有的节点数的积(若当前节点下没有节点则为1)
tree[now].b += v; //打标记
return ; //回溯
}
int mid = (l + r) >> ;
tree[now + now].b += tree[now].b; //向下传递标记(以下都是)
tree[now + now + ].b += tree[now].b;
tree[now + now].a += (tree[now].b * (mid - l + ));
tree[now + now + ].a += (tree[now].b * (r - mid));
tree[now].b = ; //向下传递标记,当前标记清空(不管怎么样就是这样)
if (y <= mid) plust(now + now, l, mid, x, y);//若操作区间在mid左边,往左边继续操作
else if (x > mid) plust(now + now + , mid + , r, x, y);//若在mid右边,往右边继续操作
else { //否则拆开分别左右操作
plust(now + now, l, mid, x, mid);
plust(now + now + , mid + , r, mid + , y);
}
tree[now].a = tree[now + now].a + tree[now + now + ].a; //更新当前节点的数据
} long long query(int now, int l, int r, int x, int y) { //区间查询
if ((l == x) && (r == y)) return tree[now].a; //若当前节点所代表的区间恰好与需要查询的区间重合,返回值
int mid = (l + r) >> ;
tree[now + now].b += tree[now].b; //同上,向下传递标记(以下都是)
tree[now + now + ].b += tree[now].b;
tree[now + now].a += (tree[now].b * (mid - l + ));
tree[now + now + ].a += (tree[now].b * (r - mid));
tree[now].b = ; //同上,向下传递标记
if (y <= mid) return query(now + now, l, mid, x, y); //查询区间在mid左边,往左边取
else if (x > mid) return query(now + now + , mid + , r, x, y); //否则在右边取
else return query(now + now, l, mid, x, mid) + query(now + now + , mid + , r, mid + , y); //再否则断开从左右边取
} int main() { //主程序
scanf("%d%d", &n, &q); //读入数列的长度和操作的次数
for (int i = ; i <= n; i++)
scanf("%d", &a[i]); build_tree(, , n); //建树 for (int i = ; i <= q; i++) {
scanf("%d%d%d", &flag, &x, &y); //开始
if (flag == ) {
scanf("%d", &v); //v为要加进去的数
plust(, , n, x, y); //区间加操作
continue;
}
printf("%lld\n", query(, , n, x, y)); //否则查询区间
} return ;
}

供个人复习使用