【模板】线段树 1

题目描述

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

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的结果。

说明

时空限制:1000ms,128M

数据规模:

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

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

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

思路:

明显,这道题是一道线段树的模板题(虽然可以拿分块什么的水过去),在题解之前,先普及一些知识

什么是线段树?

我们知道,一般数据的储存是线性的,但这样对于区间问题并不优,下图所示的数列

我们如果想要

查询,那么时间复杂度为o(区间长度)有些别跟我说用前缀和的可以优化到O(1)级别,没用的,修改时间复杂度更高

但修改呢??

如果你用正常修改,时间复杂度为O(区间长度),如果你要用前缀和,那么复杂度会为O(数列长度-区间左端);

显然,过于暴力,出题人想卡你的话你的时间复杂度会劣化到最大O(操作数*数列长度)

TLE 的飞起

那该怎么办呢?

大家学过堆吧?

一个O(nlogn^2)的暴力快排,上了二叉树就变成了O(nlogn)

快了好多啊!

那么我们这个操作能不能上树呢?

当然可以

线段树该出场了

看下面这幅图

我们成功通过二分区间的方式建立了一棵树

此时查询,时间就省得多

比如说查询1~12,暴力的话复杂度为O(12),现在只要查询【1~7】,【8~11】,【12】三个区间即可

复杂度降到O(3log14);

别看就降了这么一点,如果n很大呢?

再说说插入

你们可能会说,插入以前是O(n),你这一来成了O(nlogn),还费劲了

谁说一定要插到底来着?我这么懒就不能插到中间吗?

答案是肯定的,lazy标记该出场了

比如说上图【4~11】每个加3

结果如下

很显然,如果一个区间被要添加的区间完全包含,那你就不用再去下放到他的儿子,在他的位置上打个lazy标记jike

但同时你要注意细节,因为含有lazy标记的区间的子区间并没有被修改,所以在查询他的子区间时,lazy标记需要下放

同时如果你修改的只是一个元素,那也不需要lazy标记,直接改值即可

线段树的均摊复杂度为O(nlogn);

线段树的实现有两种方式,数组法和存边法,前者较为简单,但后者在写一些高阶线段树(比如说主席树)和平衡树时更优

 我给出一组用数组解决的方法

见代码:

#include<iostream>
#include<cstdio>
using namespace std;
struct node{
	long long lazy,sum;
}tree[400010];
int x[100010];
int n,m,a,b,c;
int as;
int sx,st,sk;
long long ans;
char pd;
void update(int l,int r,int i)//更新节点函数(就是下放函数) 
{
	if(!tree[i].lazy)
	{
		return;
	}
	int mid=(l+r)/2;
	tree[i*2].sum+=tree[i].lazy*(mid-l+1);
	tree[i*2+1].sum+=tree[i].lazy*(r-mid);
	tree[i*2].lazy+=tree[i].lazy;
	tree[i*2+1].lazy+=tree[i].lazy;
	tree[i].lazy=0;
}
long long query(int tl,int tr,int l,int r,int i)//查询函数 
{
	if(tl>r||tr<l)
	{
		return 0;
	}
	if(tl<=l&&r<=tr)
	{
		return tree[i].sum;
	}
	update(l,r,i);
	int mid=(l+r)/2;
	return query(tl,tr,l,mid,i*2)+query(tl,tr,mid+1,r,i*2+1);
}
void add(int tl,int tr,int l,int r,int i,int val)//添加函数 
{
	if(tl>r||tr<l)
	{
		return;
	}
	if(tl<=l&&tr>=r)
	{
		tree[i].sum+=val*(r-l+1);
		tree[i].lazy+=val;
		return;
	}
	update(l,r,i);
	int mid=(l+r)/2;
	add(tl,tr,l,mid,i*2,val);
	add(tl,tr,mid+1,r,i*2+1,val);
	tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
}
void build(int l,int r,int i)//运用初始数据建树 
{
	if(l==r)
	{
		tree[i].sum=x[l];
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,i*2);
	build(mid+1,r,i*2+1);
	tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
}
int main()
{
	cin>>n>>m;
	for(a=1;a<=n;a++)
	{
		cin>>x[a];
	}
	build(1,n,1);
	scanf("\n");
	for(int i=1;i<=m;i++)
	{
		char ch;
		int l,r,v;
		scanf("%c",&ch);
		if(ch=='2')
		{
			scanf("%d%d\n",&l,&r);
			ans=query(l,r,1,n,1);
			printf("%lld\n",ans);
		}
		else
		{
			scanf("%d%d%d\n",&l,&r,&v);
			add(l,r,1,n,1,v);
		}
	}
}

猜你喜欢

转载自www.cnblogs.com/ztz11/p/8979210.html