在上一篇中,我们简单的介绍了一下线段树,并介绍了它的一种用法,那就是更新单个点的值,但是如果我们要求要更新一个区间中的值,大概就是这样吧,给你一个区间让你把这个区间中的每一个值都加上a,这样一会怎么做呢?可能你会想,这个区间的应该和端点的差不多吧,多用一个for循环一个一个的去更新就行了啊。没错你说的很对,这样理论上也能解决这个题,但是关键就是时间复杂度的问题,如果我们那样做的话肯定会超时的,具体为啥我也不再解释了,仔细的想一下应该能知道上面那种方法的复杂度会非常的高的,那我们应该怎么办呢?没办法。我们不可能一个一个的去更新了啊,只能一个区间一个区间的去操作。
假如我们让1-5这个区间的所有的数字都加上4,然后我们就开始遍历这个树了,假如我们遍历到1-4这个节点,那么我们只需要让这个节点加上(4-1+1)*4就行了,然后依次往下遍历,让每一个节点都加上它应该加的数字,那么这个树的更新就完成了。那么这个方法是不是最简单的呢?我们还能不能优化呢?
我们在举一个例子,假如接连着好多的询问都是给你一个区间让你加上一个数,那么是不是对于每次的询问是不是我们每次都要把树给遍历一遍,按照上述的方法应该是这样操作的,但是聪明的你是不是发现问题呢?想一下,我们每次都要在这个节点加上一个数字,每次询问我们都要遍历一次这个树,真的是太麻烦了,我们可以把需要在这个节点加的数字先来加起来,然后等到最后我们再一并加上去,是不是这样复杂度就降低了许多呢?
在坚持一下,我们马上就要接近事情的真相了,现在我们已经简化了许多了,那么能不能再简化呢?我们在想一个问题,如果我们不需要那个节点,就是我们访问不到那个节点,那么是不是这个点加与不加对结果都是没有影响的呢?没错就是这样的。
大致的方法就是我们可以先把这个节点需要加的数字给储存到上一个节点中,如果不需要这个节点,那么我们就不更新它的值,等到需要这个节点的时候我们再更新这个点的值,这样是不是简单了许多。
下面给一道例题,来具体的展示一下这种方法的过程吧!
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 4Sample Output
4 55 9 15Hint
The sums may exceed the range of 32-bit integers.
其实这个题的意思和我们上一篇介绍的那个题是差不多的,这不过这个题把单点的更新改成了,区间的更新,下面给出AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const long long maxn=1e5+7;
long long a[maxn];
struct stu
{
long long l;
long long r;
long long sum;
long long seg;//记录这个节点及他的子节点需要加上的数字;
}A[maxn*4];
void P(long long x,long long y,long long i)//去更新这个点的子节点
{
long long mid=(x+y)/2;
A[i*2].seg+=A[i].seg;
A[i*2].sum+=((mid-x+1)*A[i].seg);
A[i*2+1].seg+=A[i].seg;
A[i*2+1].sum+=((y-mid)*A[i].seg);
A[i].seg=0;
}
void build_tree(long long x,long long y,long long i)//构建线段树;
{
A[i].l=x;
A[i].r=y;
if(x==y)
{
A[i].sum=a[x];
return ;
}
long long mid=(x+y)/2;
build_tree(x,mid,i*2);
build_tree(mid+1,y,i*2+1);
A[i].sum=A[i*2].sum+A[i*2+1].sum;
}
void G(long long x,long long y,long long val,long long i)//对所给的区间进行操作;
{
if(x<=A[i].l&&A[i].r<=y)
{
A[i].sum+=((A[i].r-A[i].l+1)*val);
A[i].seg+=val;
return ;
}
long long mid=(A[i].r+A[i].l)/2;
if(A[i].seg!=0)
P(A[i].l,A[i].r,i);
if(mid>=y)
G(x,y,val,i*2);
else if(x>mid)
G(x,y,val,i*2+1);
else
{
G(x,mid,val,i*2);
G(mid+1,y,val,i*2+1);
A[i].sum=A[i*2].sum+A[i*2+1].sum;
}
A[i].sum=A[i*2].sum+A[i*2+1].sum;
}
long long C(long long x,long long y,long long i)
{
if(x<=A[i].l&&A[i].r<=y)
return A[i].sum;
if(A[i].seg!=0)
{
P(A[i].l,A[i].r,i);
}
long long mid=(A[i].l+A[i].r)/2;
if(y<=mid)
return C(x,y,i*2);
else if(x>mid)
return C(x,y,i*2+1);
else
return C(x,mid,2*i)+C(mid+1,y,i*2+1);
}
int main()
{
long long m,n;
memset(A,0,sizeof(A));
scanf("%lld %lld",&n,&m);
for(long long i=1;i<=n;i++)
scanf("%lld",&a[i]);
build_tree(1,n,1);
while(m--)
{
char ch[10];
scanf("%s",ch);
if(ch[0]=='Q')
{
long long a1,a2;
scanf("%lld %lld",&a1,&a2);
printf("%lld\n",C(a1,a2,1));
}
else
{
long long a3,a4,a5;
scanf("%lld %lld %lld",&a3,&a4,&a5);
G(a3,a4,a5,1);
}
}
return 0;
}