一、定義
-
在線段樹中會遇到區間更新的情況,例如 在區間求和問題中,令[a,b]區間內的值全部加c,若此時再採用單點更新的方法,就會耗費大量時間,這個時候就要用到懶標記來進行區間更新了。
-
懶標記(lazy-tag),又叫做延遲標記,舉例說明。
設 當前結點對應區間[l, r],待更新區間[a, b]
當 a ≤ l ≤ r ≤ b,即 [l, r]∈[a,b]時,不再向下更新,僅更新當前結點,並在該結點加上懶標記,當必須得更新/查詢該結點的左右子結點時,再利用懶標記的記錄向下更新(pushdown)——懶標記也要向下傳遞,然後移除該結點的懶標記。
這樣就不用每次都更新到葉子結點,減少了大量非必要操作,從而優化時間複雜度。
二、模板
以下均以在區間求和問題中,令[a,b]區間內的值全部加c爲例
laz[rt]:表示其該結點rt對應區間[l, r]內的所有數據均要加上laz[rt]
1. 建樹
建樹操作與無懶標記的線段樹無異,但要記得對懶標記數組進行初始化。
int a[maxn],t[4*maxn],laz[4*maxn];
void build(int rt,int l,int r)
{
if(l==r)
{
t[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
t[rt]=t[rt<<1]+t[rt<<1|1];
}
2.向下更新
void pushdown(int rt,int l,int r) //對結點rt向下更新一層
{
if(laz[rt]==0) //laz[rt]==0直接返回
return;
//向下更新
//令左右子結點加上 laz[rt]*區間的數量
//令左右子結點的懶標記加上 laz[rt]
int mid=(l+r)>>1;
t[rt<<1]+=laz[rt]*(mid-l+1);
laz[rt<<1]+=laz[rt];
t[rt<<1|1]+=laz[rt]*(r-mid);
laz[rt<<1|1]+=laz[rt];
laz[rt]=0; //懶標記置零
}
3.區間更新
void updata(int rt,int l,int r,int a,int b,int c)
{
if(a<=l&&r<=b) //當前區間[l,r]∈待更新區間[a,b]
{
t[rt]+=c*(r-l+1); //更新當前結點,並加上懶標記
laz[rt]+=c;
return; //返回
}
pushdown(rt,l,r); //因爲還要向下一層遍歷,所有要先更新下一層
int mid=(l+r)>>1;
if(b<=mid) //待更新區間在中點(包括中點)左側 —— 向左遍歷
updata(rt<<1,l,mid,a,b,c);
else if(a>mid) //待更新區間在中點(不包括中點)右側 —— 向右遍歷
updata(rt<<1|1,mid+1,r,a,b,c);
else //否則
{
updata(rt<<1,l,mid,a,b,c);
updata(rt<<1|1,mid+1,r,a,b,c);
}
t[rt]=t[rt<<1]+t[rt<<1|1]; //不要漏了這條
}
4. 區間查詢
帶懶標記的查詢操作,就是當查詢時還需向下遍歷的時候,要先把此處懶標記向下更新。
int query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) //當前區間[l,r]∈待查詢區間[ql,qr]
return t[rt];
if(r<ql||l>qr) //區間無重合
return 0;
//區間部分重合
pushdown(rt,l,r); //先向下更新
int mid=(l+r)>>1;
return query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
}
三、模板例題
注意結果可能會超出int,要用long long存儲
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+7;
int N,Q;
LL a[maxn],t[4*maxn],laz[4*maxn];
void build(int rt,int l,int r)
{
if(l==r)
{
t[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
t[rt]=t[rt<<1]+t[rt<<1|1];
}
void pushdown(int rt,int l,int r)
{
if(laz[rt]==0)
return;
int mid=(l+r)>>1;
t[rt<<1]+=laz[rt]*(mid-l+1);
laz[rt<<1]+=laz[rt];
t[rt<<1|1]+=laz[rt]*(r-mid);
laz[rt<<1|1]+=laz[rt];
laz[rt]=0;
}
void updata(int rt,int l,int r,int a,int b,LL c)
{
if(a<=l&&r<=b)
{
t[rt]+=c*(r-l+1);
laz[rt]+=c;
return;
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(b<=mid)
updata(rt<<1,l,mid,a,b,c);
else if(a>mid)
updata(rt<<1|1,mid+1,r,a,b,c);
else
{
updata(rt<<1,l,mid,a,b,c);
updata(rt<<1|1,mid+1,r,a,b,c);
}
t[rt]=t[rt<<1]+t[rt<<1|1];
}
LL query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
return t[rt];
if(r<ql||l>qr)
return 0;
pushdown(rt,l,r);
int mid=(l+r)>>1;
return query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
}
int main()
{
scanf("%d %d",&N,&Q);
for(int i=1;i<=N;i++)
scanf("%lld",&a[i]);
build(1,1,N);
while(Q--)
{
getchar();
char C=getchar();
int a,b;
scanf("%d %d",&a,&b);
if(C=='C')
{
LL c;
scanf("%lld",&c);
updata(1,1,N,a,b,c);
}
else
printf("%lld\n",query(1,1,N,a,b));
}
return 0;
}