线段树模板1

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

  1. 将某区间每一个数加上 k。
  2. 求出某区间每一个数的和。

输入格式

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

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

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

  1. 1 x y k:将区间 [x, y][x,y] 内每个数加上 k。
  2. 2 x y:输出区间 [x, y][x,y] 内每个数的和。

输出格式

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

输入输出样例

输入

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

输出

11
8
20
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int a[maxn + 2];
struct tree{
	int l,r;
	long long pre,add;
}t[4 * maxn + 2];

void build(int p,int l,int r){
	t[p].l = l,t[p].r = r;
	if(l == r){
		t[p].pre = a[l];
		return ;
	}
	int mid = l + r >> 1;
	build(p * 2,l,mid);
	build(p * 2 + 1,mid + 1,r);
	t[p].pre = t[p * 2].pre + t[p * 2 + 1].pre;
}

void spread(int p)
{
	if(t[p].add){
		t[p * 2].pre += t[p].add * (t[p * 2].r - t[p * 2].l  + 1);
		t[p * 2 + 1].pre += t[p].add * (t[p * 2 + 1].r - t[p * 2 + 1].l + 1);
		t[p * 2].add += t[p].add;
		t[p * 2 + 1].add += t[p].add;
		t[p].add = 0;
	}
}

void change(int p,int x,int y,int z)
{
	if(x <= t[p].l && y >= t[p].r){
		t[p].pre += (long long)z * (t[p].r - t[p].l + 1);
		t[p].add += z;
		return;
	}
	spread(p);
	int mid = t[p].l + t[p].r >> 1;
	if(x <= mid)change(p * 2,x,y,z);
	if(y > mid)change(p * 2 + 1,x,y,z);
	t[p].pre = t[p * 2].pre + t[p * 2 + 1].pre;
}

long long ask(int p,int x,int y)
{
	if(x <= t[p].l && y >= t[p].r)return t[p].pre;
	spread(p);
	int mid = t[p].l + t[p].r >> 1;
	long long ans = 0;
	if(x <= mid)ans += ask(p * 2,x,y);
	if(y > mid) ans += ask(p * 2 + 1,x,y);
	return ans;
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i ++)
	scanf("%d",&a[i]);
	build(1,1,n);
	for(int i = 1;i <= m;i ++)
	{
		int q,x,y,z;
		scanf("%d",&q);
		if(q == 1){
			scanf("%d%d%d",&x,&y,&z);
			change(1,x,y,z);
		}else{
			scanf("%d%d",&x,&y);
			printf("%lld\n",ask(1,x,y));
		}
	}
	return 0;
}

posted @ 2022-09-24 21:16  zyc_xianyu  阅读(17)  评论(0编辑  收藏  举报