初学线段树--区域更新
实现原理:初始的时候,给每个树节点都给一个标记,当你在更新某一段区间的时候,要映入一个标记数组,来标记该树节点更新后是否要将更新其子节点,如果该节点的标记值不为空,则表示要对该节点的子节点进行更新,更新完后取消该节点的标记;反之,则无需更新该节点的子节点,该线段树所对应区间的值可以直接拿来使用。
下面上代码(题目来自nyoj士兵杀敌5):
#include<iostream>
#include<cstdio>
using namespace std;
typedef struct Node
{
int value;
int l;
int r;
};
Node tree[1000050<<2];
int col[1000050<<2];
int n,c,q;
int mi,ni,add,m,n1;
void build_tree(int v,int l,int r)
{
tree[v].l=l;
tree[v].r=r;
if(l==r)
{
col[v]=0;
tree[v].value=0;
return;
}
int mid=(l+r)/2;
build_tree(v<<1,l,mid);
build_tree(v<<1|1,mid+1,r);
tree[v].value=tree[v<<1].value+tree[v<<1|1].value;
}
void pushDown(int v)
{
if(col[v]!=0)
{
col[v<<1]=col[v<<1|1]=col[v];//标记传给子节点
tree[v<<1].value=(tree[v<<1].r-tree[v<<1].l+1)*col[v];
tree[v<<1|1].value=(tree[v<<1|1].r-tree[v<<1|1].l+1)*col[v];
col[v]=0;//本身撤销标记
}
}
void update(int v,int vl,int vr,int l,int r,int add)
{
if(vr<l||vl>r)
return;
if(l<=vl&&vr<=r)
{
col[v]=add;
tree[v].value+=(vr-vl+1)*add;
return;
}
pushDown(v);//进行更新,将原来的标记撤销,标记到子节点
int mid=(vl+vr)/2;
update(v<<1,vl,mid,l,r,add);
update(v<<1|1,mid+1,vr,l,r,add);
tree[v].value=tree[v<<1].value+tree[v<<1|1].value;
}
int query(int v,int vl,int vr,int l,int r)//这里注意,查询的时候也必须要进行区域更新,update结束后,
{//刚好是子节点的一个区间,这样结束的话标记还残留,因此如果查询的区间不是正好符合该子节点的区间的话
//就必须要进行子节点的更新,不然会出错
if(vl>r||vr<l)
return 0;
if(l<=vl&&vr<=r)
{
return tree[v].value;
}
pushDown(v);
int mid=(vl+vr)/2;
return query(v<<1,vl,mid,l,r)+query(v<<1|1,mid+1,vr,l,r);
}
int main()
{
scanf("%d%d%d",&n,&c,&q);
build_tree(1,1,n);
for(int i=0;i<c;i++)
{
scanf("%d%d%d",&mi,&ni,&add);
update(1,1,n,mi,ni,add);
}
for(int i=0;i<q;i++)
{
scanf("%d%d",&m,&n1);
int ans=query(1,1,n,m,n1);
printf("%d\n",ans);
}
return 0;
}