如题,已知一个数列,你需要进行下面两种操作:
第一行包含两个整数 ,,分别表示该数列数字的个数和操作的总个数。
第二行包含 个用空格分隔的整数,其中第 个数字表示数列第 项的初始值。
接下来 行每行包含 3 或 4 个整数,表示一个操作,具体如下:
1 x y k
:将区间 [,] 内每个数加上 k。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% 的数据:≤,≤
。
对于 100%的数据: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<
你可能感兴趣的:(数据结构)