题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1698
题意:自行读题
解题思想:线段树原更新一次只能更新一个叶子节点,并更新此叶子结点以上所有相关的点,当一个区间做相同更新时,叶子节点以上的相关节点不断更新,时间复杂度增加。为节省时间,为每个点添加懒惰标记。自定义节点范围(l,r为求子节点区间和),懒惰点标记(lazy储存变化值),节点和(value节点区间和)。具体实现过程:当更新一个区间时,标记该区间的父亲节点为懒惰区间(祖宗节点的值不用标记,只要更新),同时更新涵盖该区间的节点,而该区间暂时懒得去更新,等下一次更新的区间与原被标记的区间有重合时,则此时将原标记区间往下标记并更新,同时父亲节点因为更新了,没懒惰了,则取消懒惰标记,即懒惰标记传给了子节点,直到重合区间可以以子节点为单位时就可以偷懒不用往下更新了,然后以相同懒惰更新的方式更新这次要更新的区间。
样例代码图解:
下图为初始化树,圈内为value值,下标为范围。
第一次更新后1-5更新为2时后的树,红色为标记点,发现1-5的子节点并没有更新,因为计算总和只和1-5这个点有关,更新这个点就行了。
第二次更新:在访问点5-9时发现5所在的1-5点被标记了,标记点下推,并更新子节点的值。
第二次5-9区间更新后的树以及标记的点。
根据图一下子就可以看出懒惰标记的意思来了,结合代码就方便多了,我画了很久的图QAQ。
具体代码如下:
#include<bits/stdc++.h>
const int maxn=100000;
using namespace std;
struct nodetree{
int l,r;
int value,lazy;
}tree[maxn<<2];
void pushup(int now)
{
tree[now].value=tree[now<<1].value+tree[now<<1|1].value;
}
void build(int l,int r,int n)
{
tree[n].l=l;
tree[n].r=r;
tree[n].lazy=0;
if(l==r)
{
tree[n].value=1;
return;
}
int mid=(l+r)>>1;
build(l,mid,n<<1);
build(mid+1,r,n<<1|1);
pushup(n);
}
void pushdown(int n)
{
if(tree[n].lazy!=0)
{
tree[n<<1].lazy=tree[n<<1|1].lazy=tree[n].lazy;
tree[n<<1].value=(tree[n<<1].r-tree[n<<1].l+1)*tree[n<<1].lazy;
tree[n<<1|1].value=(tree[n<<1|1].r-tree[n<<1|1].l+1)*tree[n<<1|1].lazy;
tree[n].lazy=0;
}
}
void update(int l,int r,int le,int ri,int n,int va)
{
if(r<le||l>ri)
return;
if(l>=le&&r<=ri)
{
tree[n].lazy=va;
tree[n].value=tree[n].lazy*(tree[n].r-tree[n].l+1);
return;
}
pushdown(n);
int mid=(l+r)>>1;
if(mid>=le) update(l,mid,le,ri,n<<1,va);
if(mid<ri) update(mid+1,r,le,ri,n<<1|1,va);
pushup(n);
}
int main()
{
int T;
while(~scanf("%d",&T))
{
int count=1;
while(T--)
{
int n,t,l,r,va;
scanf("%d",&n);
build(1,n,1);
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&l,&r,&va);
update(1,n,l,r,1,va);
}
printf("Case %d: The total value of the hook is %d.\n",count++,tree[1].value);
}
}
return 0;
}
最后说一下点的value是通过数学计算算出来的。