题目描述
如题,已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数加上 �k。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 �,�n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 �n 个用空格分隔的整数,其中第 �i 个数字表示数列第 �i 项的初始值。
接下来 �m 行每行包含 33 或 44 个整数,表示一个操作,具体如下:
1 x y k
:将区间 [�,�][x,y] 内每个数加上 �k。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
下面提供2种版本,结构体版本,数组版本 的模板,
3372 线段树 结构体版本+数组版本 模板,区间更新,区间查询
// 3372 线段树 懒人标记版本 结构体版本
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll a[N];
struct segment{
ll val;
int l,r;
int tag;
}tree[N*4];
//计算根结点的值
void push_up(int rt)
{
tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;
}
// 创建线段树
void build(ll rt,ll left,ll right) // rt为根,left right为左右界限
{
tree[rt].l=left;
tree[rt].r=right;
tree[rt].tag=0;
if(tree[rt].l==tree[rt].r) //只有一个结点的时候,树的值就是它本身元素
{
tree[rt].val=a[left];
return ;
}
//多余一个结点,分2半,左右分别建立树,然后先上更新根
ll mid=(left+right)/2;
build(rt*2,left,mid);
build(rt*2+1,mid+1,right);
push_up(rt);//更新根
}
// 修改懒人标记 tag
void addtag(ll rt,ll left,ll right,ll d)// left-right 增加d
{
tree[rt].tag+=d;//根懒人标记:增加d ,根节点的标记增加d
tree[rt].val+=(right-left+1)*d;//树根的值 增加 数倍的d
}
void push_down(ll rt,ll left ,ll right)
{
if(tree[rt].tag>0) // 需要修改了,向下传导,递归修改
{
ll mid=(left+right)/2;
addtag(rt*2,left,mid,tree[rt].tag); // 增加的值为根节点的tag值
addtag(rt*2+1,mid+1,right,tree[rt].tag);
tree[rt].tag=0;//然后自己变成0
}
}
void update(ll l,ll r,ll rt,ll left,ll right ,ll d) //l-r 的值区间增加d
{
if(l<=left&&r>=right)//全面覆盖
{
addtag(rt,left,right,d); //直接更新 后返回,
return ;
}
// 不能够全部覆盖,向下到
push_down(rt,left,right);
ll mid=(left+right)/2;
if(l<=mid) // 包含左边
update(l,r,rt*2,left,mid,d);
if(r>mid) //包含右边
update(l,r,rt*2+1,mid+1,right,d);
push_up(rt);//然后在更新根
}
ll query(ll l,ll r,ll rt,ll left,ll right)
{
if(l<=left&&r>=right) // 全部包含 直接返回 树根
{
return tree[rt].val;
}
push_down(rt,left,right);
ll mid=(left+right)/2;
ll ans=0;
if(l<=mid)
ans+=query(l,r,2*rt,left,mid);
if(r>mid)
{
ans+=query(l,r,2*rt+1,mid+1,right);
}
return ans;
}
int main()
{
ll n,m;
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
// 建立树
build(1,1,n);//树根为1,访问1-n
while(m--)
{
ll q,l,r,d;
scanf("%lld",&q);
if(q==1) // 更新
{
scanf("%lld%lld%lld",&l,&r,&d);
update(l,r,1,1,n,d);// 根为1,范围 l-r,
}
else
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",query(l,r,1,1,n));
}
}
return 0;
}
/*
// 3372 线段树 懒人标记版本
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll a[N];
ll tree[N*4];
ll tag[N*4];//线段树一般需要4倍的空间
//计算根结点的值
void push_up(int rt)
{
tree[rt]=tree[rt*2]+tree[rt*2+1];
}
// 创建线段树
void build(ll rt,ll left,ll right) // rt为根,left right为左右界限
{
tag[rt]=0;
if(left==right) //只有一个结点的时候,树的值就是它本身元素
{
tree[rt]=a[left];
return ;
}
//多余一个结点,分2半,左右分别建立树,然后先上更新根
ll mid=(left+right)/2;
build(rt*2,left,mid);
build(rt*2+1,mid+1,right);
push_up(rt);//更新根
}
// 修改懒人标记 tag
void addtag(ll rt,ll left,ll right,ll d)// left-right 增加d
{
tag[rt]+=d;//根懒人标记:增加d
tree[rt]+=(right-left+1)*d;//树根的值 增加 数倍的d
}
void push_down(ll rt,ll left ,ll right)
{
if(tag[rt]>0) // 需要修改了,向下传导,递归修改
{
ll mid=(left+right)/2;
addtag(rt*2,left,mid,tag[rt]); // 增加的值为根节点的tag值
addtag(rt*2+1,mid+1,right,tag[rt]);
tag[rt]=0;//然后自己变成0
}
}
void update(ll l,ll r,ll rt,ll left,ll right ,ll d) //l-r 的值区间增加d
{
if(l<=left&&r>=right)//全面覆盖
{
addtag(rt,left,right,d); //直接更新 后返回,
return ;
}
// 不能够全部覆盖,向下到
push_down(rt,left,right);
ll mid=(left+right)/2;
if(l<=mid) // 包含左边
update(l,r,rt*2,left,mid,d);
if(r>mid) //包含右边
update(l,r,rt*2+1,mid+1,right,d);
push_up(rt);//然后在更新根
}
ll query(ll l,ll r,ll rt,ll left,ll right)
{
if(l<=left&&r>=right) // 全部包含 直接返回 树根
{
return tree[rt];
}
push_down(rt,left,right);
ll mid=(left+right)/2;
ll ans=0;
if(l<=mid)
ans+=query(l,r,2*rt,left,mid);
if(r>mid)
{
ans+=query(l,r,2*rt+1,mid+1,right);
}
return ans;
}
int main()
{
ll n,m;
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
// 建立树
build(1,1,n);//树根为1,访问1-n
while(m--)
{
ll q,l,r,d;
scanf("%lld",&q);
if(q==1) // 更新
{
scanf("%lld%lld%lld",&l,&r,&d);
update(l,r,1,1,n,d);// 根为1,范围 l-r,
}
else
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",query(l,r,1,1,n));
}
}
}
*/
版本3;
版本3;
// 线段树 3372 模板题目
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll a[N];
ll tree[N*4];
ll tag[N*4];//线段树一般需要4倍的空间
//计算根结点的值
void push_up(int rt)
{
tree[rt]=tree[rt*2]+tree[rt*2+1];
}
// 创建线段树
void build(ll rt,ll left,ll right) // rt为根,left right为左右界限
{
tag[rt]=0;
if(left==right) //只有一个结点的时候,树的值就是它本身元素
{
tree[rt]=a[left];
return ;
}
//多余一个结点,分2半,左右分别建立树,然后先上更新根
ll mid=(left+right)/2;
build(rt*2,left,mid);
build(rt*2+1,mid+1,right);
push_up(rt);//更新根
}
// 修改懒人标记 tag
//void addtag(ll rt,ll left,ll right,ll d)// left-right 增加d
//{
// tag[rt]+=d;//根懒人标记:增加d
// tree[rt]+=(right-left+1)*d;//树根的值 增加 数倍的d
// }
/*
//add[rt*2]+=add[rt];
//add[rt*2+1]+=add[rt];
//sum[rt*2]+=(m-m/2)*add[rt]; // 例如1-3 左边1-2 右边为3 ,左边3-3/2
//sum[rt*2+1]+=m/2*add[rt];
*/
void push_down(ll rt,int m) //更新的次数为m
{
if(tag[rt]>0) // 需要修改了,向下传导,递归修改
{
tag[rt*2]+=tag[rt];
tag[rt*2+1]+=tag[rt];
tree[rt*2]+=(m-m/2)*tag[rt];
tree[rt*2+1]+=m/2*tag[rt];
// ll mid=(left+right)/2;
// addtag(rt*2,left,mid,tag[rt]); // 增加的值为根节点的tag值
// addtag(rt*2+1,mid+1,right,tag[rt]);
tag[rt]=0;//然后自己变成0
}
}
void update(ll l,ll r,ll rt,ll left,ll right ,ll d) //l-r 的值区间增加d
{
if(l<=left&&r>=right)//全面覆盖
{
//sum[rt]+=(r-l+1)*c;
// add[rt]+=c;
tree[rt]+=(right-left+1)*d;
tag[rt]+=d;
//addtag(rt,left,right,d); //直接更新 后返回,
return ;
}
// 不能够全部覆盖,向下到
//push_down(rt,left,right);
push_down(rt,right-left+1);
ll mid=(left+right)/2;
if(l<=mid) // 包含左边
update(l,r,rt*2,left,mid,d);
if(r>mid) //包含右边
update(l,r,rt*2+1,mid+1,right,d);
push_up(rt);//然后在更新根
}
ll query(ll l,ll r,ll rt,ll left,ll right)
{
if(l<=left&&r>=right) // 全部包含 直接返回 树根
{
return tree[rt];
}
//push_down(rt,left,right);
push_down(rt,right-left+1);
ll mid=(left+right)/2;
ll ans=0;
if(l<=mid)
ans+=query(l,r,2*rt,left,mid);
if(r>mid)
{
ans+=query(l,r,2*rt+1,mid+1,right);
}
return ans;
}
int main()
{
ll n,m;
scanf("%lld %lld",&n,&m);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
// 建立树
build(1,1,n);//树根为1,访问1-n
while(m--)
{
ll q,l,r,d;
scanf("%lld",&q);
if(q==1) // 更新
{
scanf("%lld%lld%lld",&l,&r,&d);
update(l,r,1,1,n,d);// 根为1,范围 l-r,
}
else
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",query(l,r,1,1,n));
}
}
return 0;
}