题目
You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.
Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.
Output
You need to answer all Q commands in order. One answer in a line.
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
Hint
The sums may exceed the range of 32-bit integers.
题意:给定一串序列,对其进行区间增添 x x x,和查询区间和操作
思路:毫无疑问用线段树进行操作,不过这时不是对单个位置进行修改,而是对整个区间修改,因此一个一个修改,复杂度会达到 O ( n l o g n ) O(nlogn) O(nlogn),数据规模一大就会TLE。
那么怎么对区间高效的修改值呢?
我们可以用再用一个数组lazy[]来记录 t r e e [ p ] tree[p] tree[p]处的增添值,对于需要修改的区间 [ q l , q r ] [ql,qr] [ql,qr],如果当前节点 t r e e [ p ] tree[p] tree[p]的 [ l l , r r ] [ll,rr] [ll,rr]区间就在其中,那么暂时不必向下一一修改儿子,只需把增量 x x x记录在 l a z y [ p ] lazy[p] lazy[p]处, t r e e [ p ] + = ( r r − l l + 1 ) ∗ x tree[p]+=(rr-ll+1)*x tree[p]+=(rr−ll+1)∗x即可。
但如果是部分覆盖,那么情况复杂一些,必须把 t r e e [ p ] tree[p] tree[p]的 l a z y [ p ] lazy[p] lazy[p]向儿子传递,再去递归儿子求解。比如,当根节点为 [ 1 , 4 ] [1,4] [1,4],如果要修改 [ 1 , 3 ] [1,3] [1,3]的值,就需要递归至 [ 1 , 2 ] [1,2] [1,2], [ 3 , 4 ] [3,4] [3,4],而 [ 1 , 2 ] [1,2] [1,2]被完全覆盖,不必再向下修改,但 [ 3 , 4 ] [3,4] [3,4]需要向下找到3进行修改。
code:
#include <iostream>
using namespace std;
long long tree[400020];
long long input[100005];
long long lazy[400020];
int n,q;
char fuc;
void build(int p,int ll,int rr)//建树
{
lazy[p]=0;//lazy初始化为0
if (ll==rr)
{
tree[p]=input[ll];
return;
}
int mid=(ll+rr)/2;
build(2*p,ll,mid);
build(2*p+1,mid+1,rr);
tree[p]=tree[2*p]+tree[2*p+1];
return;
}
void pushdown(int p,int tot)//向下改值
{
lazy[2*p+1]+=lazy[p];//左右儿子分别标记上增量
lazy[2*p]+=lazy[p];
tree[2*p]+=(tot-tot/2)*lazy[p];//左子树所有的数增量和(线段树中右子树可能只有1个儿子)
tree[2*p+1]+=(tot/2)*lazy[p];
lazy[p]=0;
}
void change(int p,int ll,int rr,int ql,int qr,int x)
{
if (ql<=ll && qr>=rr)//如果被包含,就不用向下修改
{
lazy[p]+=x;
tree[p]+=(long long)(rr-ll+1)*x;//注意,题目中说sum会超过32位
return;
}
pushdown(p,rr-ll+1);//不满足上述条件,那么就要向下修改
int mid=(ll+rr)/2;
if (ql<=mid) change(2*p,ll,mid,ql,qr,x);//左右子树递归
if (qr>mid) change(2*p+1,mid+1,rr,ql,qr,x);
tree[p]=tree[2*p]+tree[2*p+1];//不要忘记更新tree[p]
return;
}
long long query(int p,int ll,int rr,int ql,int qr)
{
if (ql<=ll && qr>=rr)//完全包含直接返回当前结点值
return tree[p];
int mid=(ll+rr)/2;
long long res=0;
if (lazy[p]!=0)//如果有lazy标记,且查询区间不完全覆盖结点区间
pushdown(p,rr-ll+1);
if (ql<=mid) res+=query(2*p,ll,mid,ql,qr);//根据需要查左右儿子
if (qr>mid) res+=query(2*p+1,mid+1,rr,ql,qr);
return res;
}
int main()
{
cin>>n>>q;
for (int i=1;i<=n;i++)
cin>>input[i];
build(1,1,n);
while (q--)
{
cin>>fuc;
if (fuc=='Q')
{
int l,r;
cin>>l>>r;
cout<<query(1,1,n,l,r)<<endl;
}
else if (fuc=='C')
{
int l,r,add;
cin>>l>>r>>add;
change(1,1,n,l,r,add);
}
}
return 0;
}
本文介绍了一种高效的数据结构——线段树,并详细解释了如何使用懒惰传播技术来优化区间修改操作,通过一个具体的例子展示了算法的实现过程。
424

被折叠的 条评论
为什么被折叠?



