【线段树】线段树 1

L i n k Link Link

l u o g u luogu luogu P 3372 P3372 P3372

D e s c r i p t i o n Description Description

给出一堆数,形成一个区间,对于这个区间,有两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和

I n p u t Input Input

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

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

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

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

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

O u t p u t Output Output

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

S a m p l e Sample Sample I n p u t Input Input

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

S a m p l e Sample Sample O u t p u t Output Output

11
8
20

H i n t Hint Hint

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

T r a i n Train Train o f of of T h o u g h t Thought Thought

我们可以用线段树来做这一道题目,但是一般的线段树也不够,因为数据加强过,所以我们要加入 l a z y lazy lazy操作(用于存储某一个给子节点的数,当子节点被询问时再加下去)

C o d e Code Code

#include<iostream>
#include<cstdio>
using namespace std;
int N,M; long long a[100005];
struct Tree_1
{
	int l,r;
	long long lazy,val; 
}tree[400005];//l,r表示这个节点区间的左右两边,lazy表示存储的数,val表示当前区间的值
void Up(int tt)
{tree[tt].val=tree[tt*2].val+tree[tt*2+1].val;}
void Down(int tt,int m)
{
	if (tree[tt].lazy) { 
		tree[tt*2].lazy+=tree[tt].lazy;
		tree[tt*2+1].lazy+=tree[tt].lazy;
		tree[tt*2].val+=tree[tt].lazy*(m-(m/2));
		tree[tt*2+1].val+=tree[tt].lazy*(m/2);
		tree[tt].lazy=0;
	} 
}
void Build(int l,int r,int tt)
{
	tree[tt].l=l; tree[tt].r=r;
	if (l==r)
	{
		tree[tt].val=a[l];
		return;
	}
	int mid=(l+r)/2;
	Build(l,mid,tt*2);
	Build(mid+1,r,tt*2+1);
	Up(tt);//向上返回值
}//建树
void Change(int l,int r,int tt,int k)
{
	if (tree[tt].l==l && r==tree[tt].r) { //符合区间
		tree[tt].val+=(long long)(r-l+1)*k;//求区间总和
		tree[tt].lazy+=k;//lazy操作
		return ;
	} 
	if (tree[tt].l==tree[tt].r) return;
	Down(tt,tree[tt].r-tree[tt].l+1);//向下赋值
	int mid=(tree[tt].l+tree[tt].r)/2;
	if (l<=r && mid>=r) Change(l,r,tt*2,k);//剩余区间全部在左边
	 else if (l>mid && l<=r) Change(l,r,tt*2+1,k); //剩余区间全部在右边
	  else {
		  if (l<=mid) Change(l,mid,tt*2,k);//左子节点
		  if (r>mid) Change(mid+1,r,tt*2+1,k);//右子节点
	  }
	Up(tt);//向上递增
}
long long Find(int l,int r,int tt)
{
	if (tree[tt].l==l && r==tree[tt].r) return tree[tt].val;
	Down(tt,tree[tt].r-tree[tt].l+1); //向下赋值
	int mid=(tree[tt].l+tree[tt].r)/2; long long ans=0;
	if (l<=r && mid>=r) ans+=Find(l,r,tt*2);//和Change的步骤同理
	 else if (l>mid && l<=r) ans+=Find(l,r,tt*2+1); 
	  else 
	  {
	 	 if (l<=mid) ans+=Find(l,mid,tt*2);
	      if (r>mid) ans+=Find(mid+1,r,tt*2+1);
	  } 
	return ans;
}
int main()
{
	scanf("%d%d",&N,&M); 
	for (int i=1; i<=N; ++i)
	 scanf("%lld",&a[i]);
	Build(1,N,1); //建树操作
	for (int i=1; i<=M; ++i)
	{
		int t,x,y,k;
		scanf("%d%d%d",&t,&x,&y);
		if (t==1) {
			scanf("%d",&k); 
			Change(x,y,1,k);//对应操作1
		}
		 else printf("%lld\n",Find(x,y,1));//对应操作2
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值