线 段 树 1 线段树 1 线段树1
题目链接:luogu 3372
题目
已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数加上x
- 求出某区间每一个数的和
输入
第一行包含两个整数
N
N
N、
M
M
M,分别表示该数列数字的个数和操作的总个数。
第二行包含
N
N
N个用空格分隔的整数,其中第
i
i
i个数字表示数列第
i
i
i项的初始值。
接下来
M
M
M行每行包含
3
3
3或
4
4
4个整数,表示一个操作,具体如下:
操作1: 格式:
1
1
1
x
x
x
y
y
y
k
k
k 含义:将区间
[
x
,
y
]
[x,y]
[x,y]内每个数加上
k
k
k
操作2: 格式:
2
2
2
x
x
x
y
y
y 含义:输出区间
[
x
,
y
]
[x,y]
[x,y]内每个数的和
输出
输出包含若干行整数,即为所有操作 2 2 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
%
30%
30%的数据:
N
<
=
8
,
M
<
=
10
N<=8,M<=10
N<=8,M<=10
对于
70
%
70%
70%的数据:
N
<
=
1000
,
M
<
=
10000
N<=1000,M<=10000
N<=1000,M<=10000
对于
100
%
100%
100%的数据:
N
<
=
100000
,
M
<
=
100000
N<=100000,M<=100000
N<=100000,M<=100000
(数据已经过加强,保证在
i
n
t
64
/
l
o
n
g
l
o
n
g
int64/long long
int64/longlong数据范围内)
思路
这道题就是一道线段树的模板题。
我们就分为建树、区间求和、区间加值、传递加值、懒标记这五个部分,来做,就可以了。
而具体的过程,大家了解一下线段树就知道了:
线
段
树
知
识
链
接
线段树知识链接
线段树知识链接
代码
#include<cstdio>
#define ll long long
using namespace std;
struct tr
{
ll l,r,sum,lan;
}tree[400001];
ll n,m,he,x,y,z,ans;
void build(ll l,ll r,ll now)//建树
{
tree[now].l=l;tree[now].r=r;
if (l==r)
{
scanf("%lld",&tree[now].sum);
return ;
}
ll mid=(l+r)/2;
build(l,mid,now*2);
build(mid+1,r,now*2+1);
tree[now].sum+=tree[now*2].sum+tree[now*2+1].sum;
}
void up(ll now,ll sum)//把加的值向上传递
{
if (!now) return ;
tree[now].sum+=sum;
up(now/2,sum);
}
void down(ll now)//懒标记
{
tree[now*2].lan+=tree[now].lan;
tree[now*2+1].lan+=tree[now].lan;
tree[now*2].sum+=tree[now].lan*(tree[now*2].r-tree[now*2].l+1);
tree[now*2+1].sum+=tree[now].lan*(tree[now*2+1].r-tree[now*2+1].l+1);
tree[now].lan=0;
}
void sum(ll now)//区间求和
{
if (tree[now].l>=x&&tree[now].r<=y)
{
ans+=tree[now].sum;
return ;
}
if (tree[now].lan) down(now);
ll mid=(tree[now].l+tree[now].r)/2;
if (x<=mid) sum(now*2);
if (y>mid) sum(now*2+1);
}
void add(ll now)//区间加值
{
if (tree[now].l>=x&&tree[now].r<=y)
{
tree[now].sum+=z*(tree[now].r-tree[now].l+1);
tree[now].lan+=z;
up(now/2,z*(tree[now].r-tree[now].l+1));
return ;
}
if (tree[now].lan) down(now);
ll mid=(tree[now].l+tree[now].r)/2;
if (x<=mid) add(now*2);
if (y>mid) add(now*2+1);
}
int main()
{
scanf("%lld%lld",&n,&m);//读入
build(1,n,1);//建树
for (ll i=1;i<=m;i++)
{
scanf("%lld",&he);//读入
if (he==1)
{
scanf("%lld%lld%lld",&x,&y,&z);//读入
add(1);//区间加值
}
else
{
scanf("%lld%lld",&x,&y);//读入
ans=0;//初始化
sum(1);//区间求和
printf("%lld\n",ans);//输出
}
}
return 0;
}