初学线段树--区域更新
实现原理:初始的时候,给每个树节点都给一个标记,当你在更新某一段区间的时候,要映入一个标记数组,来标记该树节点更新后是否要将更新其子节点,如果该节点的标记值不为空,则表示要对该节点的子节点进行更新,更新完后取消该节点的标记;反之,则无需更新该节点的子节点,该线段树所对应区间的值可以直接拿来使用。
下面上代码(题目来自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; }