线段树区间修改与区间查询,如果我们对区间中的每一个值依次进行修改,那么最终结果一定是tle。因此我们在这里加入lazy标记。
lazy标记,对于区间更新而言,如果我首次更新这段区域,我就先将需要更新的值加到lazy标签中,暂时不向子节点更新。在第二次更新过程中,我们查到此点,则顺手将此点的lazy标签中的值,更新到子节点,然后再重置lazy标签。这样一来,就将区间更新的时间复杂度,进一步降低。
贴一下模板:
long long tr[M*4],a[M],add[M*4];
void pushdown(int i,int len)
{
if(add[i])
{
add[i<<1]+=add[i];
add[i<<1|1]+=add[i];
tr[i<<1]+=add[i]*(len-len/2);
tr[i<<1|1]+=add[i]*(len/2);
add[i]=0;
}
}
void pushup(int i)
{
tr[i]=tr[i*2]+tr[i*2+1];
}
void build(int i,int l,int r)
{
if(l==r)
{
tr[i]=a[l];
return;
}
int mid =(l+r)>>1;
build(2*i,l,mid);
build(2*i+1,mid+1,r);
pushup(i);
}
void range_update(int i,int l,int r,int x,int y,int t)
{
if(x<=l&&y>=r)
{
add[i]+=t;
tr[i]=tr[i]+(r-l+1)*t;
return;
}
pushdown(i,r-l+1);
int mid=(l+r)>>1;
if(x<=mid) range_update(2*i,l,mid,x,y,t);
if(y>mid) range_update(2*i+1,mid+1,r,x,y,t);
pushup(i);
}
long long query(int i,int l,int r,int x,int y)
{
long long sum=0;
if(x<=l&&y>=r)
{
return tr[i];
}
pushdown(i,r-l+1);
int mid=(r+l)>>1;
if(x<=mid)sum+=query(2*i,l,mid,x,y);
if(y>mid) sum+=query(2*i+1,mid+1,r,x,y);
pushup(i);
return sum;
}
线段树例题:
nefu1475:
这道题是对于区间进行sqrt()操作,sqrt()操作是没办法用区间更新的,lazy没办法对一个区间sqrt进行操作。
对于一个值进行sqrt操作很快就可以将原本的值变为1,如果变为1了,显然不需要再次进行sqrt操作了,那么,我们对于整个区间进行标记,如果这个区间都不需要更新,那么,我们直接跳出,那不就加快了程序的运行吗。
那么我们建立一个新的数组flag数组,对于这个数组跟tr数组结构相同,同时,其中储存的是我们想得到的区间是否需要更新,如果,需要区间内所有的值不是0,就是1,那么就说明这个区间不需要更新,那么我们就可以直接跳出。
#include <cstdio>
#include <cstring>
#include <cctype>
#include <string>
#include <set>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
const int M=2e5+5;
ll tr[M*4];
int flag[M*4],n,m;
void pushup(int i)
{
tr[i]=tr[2*i]+tr[2*i+1];
flag[i]=flag[i*2+1]&flag[i*2];//对flag区间进行&运算,对于非0即1的flag数组,如果全是1则为1,其他情况都为0,都需要更新
}
void build(int i,int l,int r)
{
if(l==r)
{
scanf("%lld",&tr[i]);
if(tr[i]<=1) flag[i]=1;//如果是1,或者0那么我们直接将flag变成1,这一段我们就不更新了
return;
}
int mid=(r+l)>>1;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
pushup(i);
}
void update(int i,int l,int r,int x,int y)//更新时直接对这个区间进行查询更新,如果这个区间所有值都为1或0那么我们就不更新直接跳出。
{
if(flag[i]==1)return;
if(l==r)
{
tr[i]=(ll)sqrt(tr[i]);
if(tr[i]<=1) flag[i]=1;
return;
}
int mid=(r+l)>>1;
if(x<=mid) update(i*2,l,mid,x,y);
if(y>=mid+1) update(i*2+1,mid+1,r,x,y);
pushup(i);
}
ll query(int i,int l,int r,int x,int y)
{
ll ans=0;
if(x<=l&&y>=r)
{
return tr[i];
}
int mid=(r+l)/2;
if(x<=mid)ans+=query(i*2,l,mid,x,y);
if(y>mid) ans+=query(i*2+1,mid+1,r,x,y);
return ans;
}
int main()
{
while(~scanf("%d",&n))
{
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int x,y,o;
scanf("%d%d%d",&o,&x,&y);
if(o==1)
{
printf("%lld\n",query(1,1,n,x,y));
}
else
{
update(1,1,n,x,y);
}
}
}
return 0;
}
我认为,flag数组跟lazy数组有一定的相似性。都与tr有着相同的结构,对于区间修改降低时间复杂度服务。