一般线段树做区间修改操作时,先是找到目标区间,然后修改该区间,并打下延迟标记,最后从目标区间自底向上,更新所有包含目标区间的区间的值(即pushup)。当该区间子节点被访问前,pushdown下推标记。
这种维护区间的方式存在一点点弊端。例如用这种方式写一棵可持久化线段树,因为每次pushdown都相当于继续之前延迟了的更新操作,所以得开新的节点,然后开着开着可能就爆内存了…..
一种更好的方法就是用标记永久化。 从字面意思就可以知道,永久化的标记是不会变的,这个不变是不会变小(一般的那种 pushdown的时候标记都清0了)。
实现过程:update[L,R]时,把所有包含[L,R]的区间更新(显然修改[L,R]能影响到的就是这些区间),然后在[L,R]上打上标记;query[L,R]时,自顶向下找区间[L,R],统计从根到目标节点的路上标记的和,结果就是目标区间的值加上路径上所有标记的影响。
优势:不用pushdown,代码短了,且可持久化的时候不用开新的节点;与下推标记的写法相比略快一点
下面附上poj3468和hdu4348的代码
poj3468
#include
#include
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 100000+10;
typedef long long ll;
ll t[maxn<<2],lazy[maxn<<2];
void build(int l,int r,int rt)
{
lazy[rt] = 0;
if(l==r)
{
scanf("%lld",&t[rt]);
return ;
}
int m = l+r >> 1;
build(lson);
build(rson);
t[rt] = t[rt<<1] + t[rt<<1|1];
}
void update(int L,int R,int val,int l,int r,int rt)
{
t[rt] += (ll)(R-L+1) * val;
if(L==l&&r==R)
{
lazy[rt] += val;
return ;
}
int m = l + r >> 1;
if(R<=m)
update(L,R,val,lson);
else if(L>m)
update(L,R,val,rson);
else update(L,m,val,lson), update(m+1,R,val,rson);
}
ll query(int L,int R,int l,int r,int rt,int add)
{
if(L==l&&r==R)
return t[rt] + (ll)add*(r-l+1);
int m = l + r >> 1;
if(R<=m)
return query(L,R,lson,add+lazy[rt]);
else if(L>m)
return query(L,R,rson,add+lazy[rt]);
else return query(L,m,lson,add+lazy[rt]) + query(m+1,R,rson,add+lazy[rt]);
}
int N,Q;
int x,y;
ll z;
char str[10];
int main()
{
scanf("%d%d",&N,&Q);
build(1,N,1);
while(Q--)
{
scanf("%s",str);
if(str[0]=='C')
{
scanf("%d%d%d",&x,&y,&z);
update(x,y,z,1,N,1);
}
else
{
scanf("%d%d",&x,&y);
printf("%lld\n",query(x,y,1,N,1,0));
}
}
return 0;
}
hdu4348
#include
#include
#include
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
int lazy[maxn*30],ls[maxn*30],rs[maxn*30],N,Q;
ll t[maxn*30];
int tot,root[maxn],nowroot;
#define lson l,m,ls[now]
#define rson m+1,r,rs[now]
void build(int l,int r,int &now)
{
now = ++tot;
lazy[now] = 0;
if(l==r)
{
scanf("%I64d",&t[now]);
//printf("t[%d] = %lld\n",now,t[now]);
return ;
}
int m = l + r >> 1;
build(lson);
build(rson);
t[now] = t[ls[now]] + t[rs[now]];
//printf("t[%d] = %lld\n",now,t[now]);
}
void update(int L,int R,int val,int l,int r,int &now,int lst)
{
now = ++tot;
ls[now] = ls[lst], rs[now] = rs[lst];
t[now] = t[lst] + (ll)(R-L+1)*val;
lazy[now] = lazy[lst];
if(L == l && r == R)
{
lazy[now] += val;
return ;
}
int m = l + r >> 1;
if(R<=m)
update(L,R,val,lson,ls[lst]);
else if(L>m)
update(L,R,val,rson,rs[lst]);
else
update(L,m,val,lson,ls[lst]), update(m+1,R,val,rson,rs[lst]);
}
ll query(int L,int R,int l,int r,int now,int add)
{
if(L == l && r == R)
return t[now] + (ll)(R-L+1)*add;
int m = l + r >> 1;
if(R<=m)
return query(L,R,lson,add+lazy[now]);
else if(L>m)
return query(L,R,rson,add+lazy[now]);
else
return query(L,m,lson,add+lazy[now]) + query(m+1,R,rson,add+lazy[now]);
}
void init()
{
tot = 0;
nowroot = 1;
}
int main()
{
char op[2];
int l,r,v;
while(scanf("%d%d",&N,&Q)!=EOF)
{
init();
build(1,N,root[1]);
while(Q--)
{
scanf("%s",op);
switch(op[0])
{
case 'Q':
scanf("%d%d",&l,&r);
printf("%I64d\n",query(l,r,1,N,root[nowroot],0));
break;
case 'C':
scanf("%d%d%d",&l,&r,&v);
update(l,r,v,1,N,root[nowroot+1],root[nowroot]);
++nowroot;
break;
case 'H':
scanf("%d%d%d",&l,&r,&v);
printf("%I64d\n",query(l,r,1,N,root[v+1],0));
break;
default:
scanf("%d",&v);
tot = root[v+2] - 1;//回收内存
nowroot = v+1;
break;
}
}
}
}