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
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 100005;

int n, m,a[maxn*2];
struct tree
{
	int left, right;//左端点,右端点
	long long value, tag;//维护的值(这题的区间和)以及懒标志

}t[maxn*4];

void build(int p, int x, int y)//构建树
{
	t[p].left = x;//第p个节点的左端点
	t[p].right = y;//..右端点同上

	if(t[p].left == t[p].right)//叶子
	{
		t[p].value = a[x];
		return ;
	}
	int mid = (x + y)/2;
	build(2*p, x, mid);//构造左子树
	build(2*p + 1, mid +1, y);//构造右子树
	t[p].value = t[2*p].value + t[2*p + 1].value;//改变维护的值

}

 

//其实懒标记是表示树节点表示的这一段区间数字要加,也就是左子树和右子树都要加
//但是现在暂时不加,因为询问的时候未必用到左子树或者右子树的区间
//要的时候再调用这个函数用懒标记去修改,即下传懒标记
void spread(int p)//懒标志下放
{
	if(t[p].tag)
	{
		t[2*p].value += t[p].tag*(t[2*p].right - t[2*p].left + 1);//改变左孩子维护的值
		t[2*p + 1].value += t[p].tag *(t[2*p + 1].right - t[2*p+1].left + 1);//同上

		t[2*p].tag += t[p].tag;//放懒标记
		t[2*p + 1].tag += t[p].tag;//同上
		t[p].tag  = 0;//父节点懒标记清零
	}
}
void change(int p, int x, int y, int z)
{
	if(x<=t[p].left && y >= t[p].right)//区间覆盖
	{
		t[p].value += (long long )z * (t[p].right - t[p].left + 1);//修改维护的值
		t[p].tag += z;//打上懒标记
		return ;
	}
	spread(p);//下方懒标记
	int mid = (t[p].left + t[p].right) >> 1;
	if(x <= mid)
		change(2*p, x, y, z);//修改区间覆盖左孩子,修改左孩子
	if(y > mid)//同理
		change(2*p + 1, x, y, z);

	t[p].value = t[p*2].value + t[2*p + 1].value;

}

long long sum(int p, int x, int y)
{
	if(x <= t[p].left && y >= t[p].right)//区间覆盖
	{
		return t[p].value;
	}
	spread(p);//下方懒标记
	long long cnt = 0;
	int mid = (t[p].left + t[p].right) >> 1;
	if(x <= mid)
		cnt += sum(2*p, x, y);
	if(y > mid)
		cnt += sum(2*p + 1, x, y);
	return cnt;

}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i = 1; i <= n; i++)
		cin>>a[i];
	int q, x, y,z;
	build(1, 1, n);
	/*for(int i =1; i <= 16; i++)
		cout<<t[i].left<<" "<<t[i].right<<endl;
	system("pause");*/
	while(m--)
	{
		cin>>q;
		if(q == 1)
		{
			cin>>x>>y>>z;
			change(1, x, y, z);
		}
		else
		{
			cin>>x>>y;
			cout<<sum(1, x, y)<<endl;
		}


	}
	

	system("pause");

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值