您的位置:首页 > 其它

线段树之区间更新

2015-08-09 21:57 155 查看

线段树之区间更新

线段树系列上一篇文章讲了基础的线段树的建树,单点更新,区间查询。那这篇文章主要讲线段树的区间更新,也就是延迟更新。其实延迟更新的本质和单点更新差不多,只不过差别在于单点更新每次都递归到底,但是区间更新则是做一个延迟标记,等到下次更新或查询的时候再去判断是不是往下更新。

为什么这样?答案是显然的。线段树的查询和单点更新的时间复杂度是O(logn)的,如果我每次都只是更新一个节点,再去询问,那么复杂度是O(nlogn),这个复杂度是可以接受的,但是如果每次操作我都是更新一个区间,还是用单点更新去做,那么复杂度是O(n^2logn),这个复杂度是很大的,一般来说,在ACM竞赛中是很难接受的或者说是直接摒弃的。所以我们要想办法去降低复杂度。于是我们就有了区间更新。

以下我们以给某个区间增加x为例讲解:

假设我要更新区间[a,b],那我开始由根节点开始递归查找区间,如果发现区间[l,r]完全包含在区间[a,b]里,我就在这停止,不在继续更新下去,并在这个节点做标记记录x,并更新这个节点的sum值。如果下次查询的时候,碰见标记就开始更新左右儿子,并把自己的标记消除,给儿子做标记,以此类推,直到找到符合查找的区间为止。

这样为什么会快,当节点数很多的时候,如果我每次都把整个操作区间更新到底就会很浪费时间,通过标记,我可以更新到某一层就停止,不必更新到底,这样当然就很节省时间了。

我们就以这个题意编写代码讲解:

题意:在一组数中执行两种操作:

(1) "C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.

(2)"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.

const int maxn=10005;//假设点的个数
struct seg_tree//为线段树定义一个结构体
{
int sum,seg;//sum表示区间和,seg作为标记
}tree[4*maxn];

//更新父亲节点操作
void pushup(int root)
{
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
return ;
}

//更新左右儿子操作
void pushdown(int root,int l,int r)
{
int mid=(l+r)>>1;
//更新左儿子
tree[root<<1].seg+=tree[root].seg;
tree[root<<1].sum+=(mid-l+1)*tree[root].seg;
//更新右儿子
tree[root<<1|1].seg+=tree[root].seg;
tree[root<<1|1].sum+=(r-(mid+1)+1)*tree[root].seg;
//消除父亲节点的标记
tree[root].seg=0;

return ;
}

//建树操作
void build_tree(int root,int r,int l)
{
tree[root].seg=0;

if(l==r)
{
tree[root].sum=A[l];
return ;
}

int mid=(r+l)>>1;

build_tree(root<<1,l,mid);
build_tree(root<<1|1,mid+1,r);

pushup(root);
}

//更新操作
void update(int root,int l,int r,int L,int R,int val )//L,R表示更新的区间的端点,val表示要加的值
{
if(L<=l&&r<=R) //如果被操作区间完全包含,就不用往下更新了
{
tree[root].sum+=(r-l+1)*val;
tree[root].seg+=val;
return ;
}

if(tree[root].seg!=0)   {  pushdown(root,l,r);}//延迟更新

int mid=(r+l)>>1;

if(L<=mid)
update(root<<1,l,mid,L,R,val);
if(mid<R)
update(root<<1|1,mid+1,r,L,R,val);

pushup(root);
}

//查询操作
int query(int root,int l,int r,int L,int R)//L,R表示查询的区间的端点
{
if(L<=l&&r<=R)
{
return tree[root].sum;
}

if(tree[root].seg!=0)//如果有标记就需要往下更新
pushdown(root,l,r);

int mid=(r+l)>>1,sum=0;

if(L<=mid)
sum+=query(root<<1,l,mid,L,R);
if(mid<R)
sum+=query(root<<1|1,mid+1,r,L,R);

return sum;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: