洛谷P3372 【模板】线段树 1

洛谷题目传送门

题目描述

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

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

输入格式

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

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

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

  1. 1 x y k:将区间 [,] 内每个数加上 k。
  2. 2 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

说明/提示

对于 30% 的数据:≤8,≤10。
对于 70% 的数据:≤10^{3},≤10^{4}
对于 100%的数据:1≤,≤10^{5}

保证任意时刻数列中所有元素的绝对值之和 ≤10^{18}

样例解释

洛谷P3372 【模板】线段树 1_第1张图片

题目解读

        题目已经说得很清楚了,就是线段树模板。由于需要区间修改,我们需要用到懒标记

线段树模板

class edge_tree {//线段树
public:
	void pushup_sum(ll u,S *tree){tree[u].data=tree[u<<1].data+tree[u<<1|1].data;}//前缀和中更新 tree[u].data
	void add_sum(ll u,ll v,S *tree){tree[u].lzy+=v;tree[u].data+=(tree[u].r-tree[u].l+1)*v;}//给 u 所对应区间加上 v
	void pushdown(ll u,S *tree){//懒标记下传
		if(!tree[u].lzy)return;//如果未打上懒标记,跳过
		add_sum(u<<1,tree[u].lzy,tree);add_sum(u<<1|1,tree[u].lzy,tree);//分别传到左、右子树
		tree[u].lzy=0;//已经下传了就清空标记
	}
	void build(ll u,ll l,ll r,S *tree){//建立线段树
		tree[u]=(S){l,r,0};//节点 u 所对区间为 [l,r]
		if(l==r){tree[u].data=a[l];tree[u].lzy=0;return;}//如果 u 为叶子节点,tree[u].data 就是 a[l],而且叶子节点无左右子树,跳过建左右子树
		ll mid=(l+r)/2;
		build(u<<1,l,mid,tree);build(u<<1|1,mid+1,r,tree);//分别建立左右子树
		pushup_sum(u,tree);//更新 tree[u].data
	}
	void chang_jia(ll u,ll x,ll y,ll v,S *tree){//将区间 [x,y] 中的元素加上 v
		if(tree[u].l>y||tree[u].r=x&&tree[u].r<=y){add_sum(u,v,tree);return;}//如果 [x,y] 完全包含当前区间,当前区间直接加上 v
		pushdown(u,tree);//标记下传(下面要更新子树了,不下传就废了)
		chang_jia(u<<1,x,y,v,tree);chang_jia(u<<1|1,x,y,v,tree);//分别修改左右子树
		pushup_sum(u,tree);//更新 tree[u].data
	}
	ll get_sum(ll u,ll x,ll y,S *tree){//求区间 [x,y] 的和
		ll l=tree[u].l,r=tree[u].r;
		if(yr)return 0;//如果当前区间与 [x,y] 无交集,跳过
		if(x<=l&&r<=y)return tree[u].data;//如果 [x,y] 完全包含当前区间,直接返回当前区间的和
		pushdown(u,tree);//标记下传(同上)
		return get_sum(u<<1,x,y,tree)+get_sum(u<<1|1,x,y,tree);//分别遍历左右子树
	}
};

完整代码

#include
using namespace std;
#define ll long long
const ll N=1e5+5;
ll n,m,a[N];
struct S{
	ll l,r,data,lzy;// l 为左边界,r 为右边界,lzy 为懒标记,data 为当前节点的值(区间和,区间最值)
};
class edge_tree {//线段树
private:
	S tree[N*4];
public:
	void pushup_sum(ll u){tree[u].data=tree[u<<1].data+tree[u<<1|1].data;}//前缀和中更新 tree[u].data
	void add_sum(ll u,ll v){tree[u].lzy+=v;tree[u].data+=(tree[u].r-tree[u].l+1)*v;}//给 u 所对应区间加上 v
	void pushdown(ll u){//懒标记下传
		if(!tree[u].lzy)return;//如果未打上懒标记,跳过
		add_sum(u<<1,tree[u].lzy);add_sum(u<<1|1,tree[u].lzy);//分别传到左、右子树
		tree[u].lzy=0;//已经下传了就清空标记
	}
	void build(ll u,ll l,ll r){//建立线段树
		tree[u]=(S){l,r,0};//节点 u 所对区间为 [l,r]
		if(l==r){tree[u].data=a[l];tree[u].lzy=0;return;}//如果 u 为叶子节点,tree[u].data 就是 a[l],而且叶子节点无左右子树,跳过建左右子树
		ll mid=(l+r)/2;
		build(u<<1,l,mid);build(u<<1|1,mid+1,r);//分别建立左右子树
		pushup_sum(u);//更新 tree[u].data
	}
	void chang_jia(ll u,ll x,ll y,ll v){//将区间 [x,y] 中的元素加上 v
		if(tree[u].l>y||tree[u].r=x&&tree[u].r<=y){add_sum(u,v);return;}//如果 [x,y] 完全包含当前区间,当前区间直接加上 v
		pushdown(u);//标记下传(更新子树)
		chang_jia(u<<1,x,y,v);chang_jia(u<<1|1,x,y,v);//分别修改左右子树
		pushup_sum(u);//更新 tree[u].data
	}
	ll get_sum(ll u,ll x,ll y){//求区间 [x,y] 的和
		ll l=tree[u].l,r=tree[u].r;
		if(yr)return 0;//如果当前区间与 [x,y] 无交集,跳过
		if(x<=l&&r<=y)return tree[u].data;//如果 [x,y] 完全包含当前区间,直接返回当前区间的和
		pushdown(u);//标记下传(同上)
		return get_sum(u<<1,x,y)+get_sum(u<<1|1,x,y);//分别遍历左右子树
	}
}etree;
int main(){
	cin>>n>>m;
	for(ll i=1;i<=n;i++)cin>>a[i];
	etree.build(1,1,n);
	ll op,x,y,z;
	for(ll i=1;i<=m;i++){
		cin>>op;
		if(op==1){cin>>x>>y>>z;etree.chang_jia(1,x,y,z);}
		else {cin>>x>>y;cout<

你可能感兴趣的:(数据结构)