线段树 - A Simple Problem with Integers - POJ - 3468
题意:
给 定 长 度 为 N 的 序 列 A 1 , A 2 , . . . , A N , 共 有 两 种 操 作 : 给定长度为N的序列A_1,A_2,...,A_N,共有两种操作: 给定长度为N的序列A1,A2,...,AN,共有两种操作:
1 、 “ C l r d ” , 表 示 把 A [ l ] , A [ l + 1 ] , … , A [ r ] 都 加 上 d 。 1、“C \ l\ r\ d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。 1、“C l r d”,表示把A[l],A[l+1],…,A[r]都加上d。
2 、 “ Q l r ” , 表 示 询 问 数 列 中 区 间 [ l , r ] 的 数 的 和 。 2、“Q \ l\ r”,表示询问 数列中区间 [l,r] 的数的和。 2、“Q l r”,表示询问数列中区间[l,r]的数的和。
共 Q 组 询 问 。 共Q组询问。 共Q组询问。
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
数据范围:
1
≤
N
,
Q
≤
100000
,
−
1000000000
≤
A
i
≤
1000000000
,
−
10000
≤
d
≤
10000.
1 ≤ N,Q ≤ 100000,-1000000000 ≤ A_i ≤ 1000000000,-10000 ≤ d ≤ 10000.
1≤N,Q≤100000,−1000000000≤Ai≤1000000000,−10000≤d≤10000.
T
i
m
e
l
i
m
i
t
:
5000
m
s
,
C
a
s
e
t
i
m
e
l
i
m
i
t
:
2000
m
s
,
M
e
m
o
r
y
l
i
m
i
t
:
131072
k
B
Time \ limit:5000 ms,Case \ time \ limit:2000 ms,Memory \ limit:131072 kB
Time limit:5000ms,Case time limit:2000ms,Memory limit:131072kB
分析:
两
种
操
作
分
别
是
区
间
修
改
和
区
间
求
和
,
可
以
通
过
差
分
数
组
,
转
化
为
单
点
修
改
,
再
通
过
树
状
数
组
求
前
缀
和
。
两种操作分别是区间修改和区间求和,可以通过差分数组,转化为单点修改,再通过树状数组求前缀和。
两种操作分别是区间修改和区间求和,可以通过差分数组,转化为单点修改,再通过树状数组求前缀和。
详
见
—
—
详见——
详见——《树状数组 - 一个简单的整数问题》。
这 里 用 线 段 树 来 解 决 这 个 问 题 。 这里用线段树来解决这个问题。 这里用线段树来解决这个问题。
做 区 间 加 法 , 需 要 在 节 点 结 构 体 中 添 加 懒 标 记 a d d 。 做区间加法,需要在节点结构体中添加懒标记add。 做区间加法,需要在节点结构体中添加懒标记add。
表 示 某 段 区 间 内 的 所 有 数 都 增 加 a d d 。 表示某段区间内的所有数都增加add。 表示某段区间内的所有数都增加add。
需 要 注 意 的 是 , 懒 标 记 只 有 在 查 询 或 者 修 改 子 区 间 的 时 候 , 才 会 向 下 传 递 。 每 次 传 递 后 , 要 将 父 节 点 的 懒 标 记 清 空 。 需要注意的是,懒标记只有在查询或者修改子区间的时候,才会向下传递。每次传递后,要将父节点的懒标记清空。 需要注意的是,懒标记只有在查询或者修改子区间的时候,才会向下传递。每次传递后,要将父节点的懒标记清空。
因 为 我 们 要 保 证 每 个 节 点 表 示 的 区 间 内 的 懒 标 记 要 相 同 , 不 能 整 个 区 间 的 几 段 a d d 的 值 不 同 。 因为我们要保证每个节点表示的区间内的懒标记要相同,不能整个区间的几段add的值不同。 因为我们要保证每个节点表示的区间内的懒标记要相同,不能整个区间的几段add的值不同。
对 懒 标 记 有 个 p u s h d o w n 操 作 , 记 父 节 点 为 r o o t , 向 下 传 递 懒 标 记 a d d : 对懒标记有个pushdown操作,记父节点为root,向下传递懒标记add: 对懒标记有个pushdown操作,记父节点为root,向下传递懒标记add:
左 区 间 l e f t : l e f t . a d d + = r o o t . a d d , l e f t . s u m + = c n t l e f t × l e f t . a d d , 其 中 c n t l e f t 为 左 区 间 中 数 的 个 数 。 左区间left:left.add+=root.add,left.sum+=cnt_{left}×left.add,其中cnt_{left}为左区间中数的个数。 左区间left:left.add+=root.add,left.sum+=cntleft×left.add,其中cntleft为左区间中数的个数。
右 区 间 同 理 。 最 后 不 要 忘 记 清 空 r o o t . a d d = 0 。 右区间同理。最后不要忘记清空root.add=0。 右区间同理。最后不要忘记清空root.add=0。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
struct node
{
int l,r;
ll sum,add;
}tr[N*4];
int n,m;
int w[N];
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u)
{
node &root=tr[u], &left=tr[u<<1], &right=tr[u<<1|1];
if(root.add)
{
left.add+=root.add, left.sum+=(ll)(left.r-left.l+1)*root.add;
right.add+=root.add, right.sum+=(ll)(right.r-right.l+1)*root.add;
root.add=0;
}
}
void build(int u,int l,int r)
{
if(l==r) tr[u]={l,r,w[r],0};
else
{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int d)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].sum+=(ll)(tr[u].r-tr[u].l+1)*d;
tr[u].add+=d;
}
else //区间分裂开了,有几段的懒标记不同
{
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,d);
if(r>mid) modify(u<<1|1,l,r,d);
pushup(u);
}
}
ll query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
ll sum=0;
if(l<=mid) sum+=query(u<<1,l,r);
if(r>mid) sum+=query(u<<1|1,l,r);
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
char op[2];
int l,r,d;
while(m--)
{
scanf("%s%d%d",op,&l,&r);
if(*op=='Q') printf("%lld\n",query(1,l,r));
else
{
scanf("%d",&d);
modify(1,l,r,d);
}
}
return 0;
}