定义
线段树是以一段区间为节点的树。
设线段树的一个非叶子节点 k k k , k k k 的儿子节点为 k < < 1 , k < < 1 ∣ 1 k<<1 ,k<<1|1 k<<1,k<<1∣1 。
若节点 k k k 表示的是区间 l l l 至 r r r , 则节点 k < < 1 , k < < 1 ∣ 1 k<<1 ,k<<1|1 k<<1,k<<1∣1 分别表示的是区间 l l l 至 m i d mid mid 和 m i d + 1 mid+1 mid+1 至 r r r 。
m i d mid mid 为 ( l + r ) > > 1 (l+r)>>1 (l+r)>>1 。
叶子节点表示的是某一个点的值。
接下来以求动态区间的某一段的和为例。
建树
void build(ll k,ll l,ll r)
{
if(l==r)//叶子节点
{
sum[k]=a[l];
return;
}
else
{
int mid=(l+r)/2;//先求出儿子节点的的值,然后该区间的和为两个儿子节点的和
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
}
区间查询,单点修改
ll query(ll k,ll l,ll r,ll x,ll y)
{
if(l>=x&&r<=y)
{
return sum[k];//节点k在查询的区间x至y内
}
ll mid=(l+r)/2;
ll res=0;
if(x<=mid)res+=query(k<<1,l,mid,x,y);//区间x至y与区间l至mid有交集
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);//区间x至y与区间mid+1至r有交集
return res;
}
void update(ll k,ll l,ll r,ll x,ll y)
{
if(r<x||l>x)return;
if(l==r&&l==x)
{
sum[x]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid) update(k>>1,l,mid,x,y);//同理
else if(x>mid) update(k>>1|1,mid+1,r,x,y);
sum[k]=sum[k>>1]+sum[k>>1|1;
return;
}
区间修改,区间查询(只含加减)
这里引入懒标记-- l a z y — t a g lazy\text{---}tag lazy—tag。
每一次修改先在包含节点 k k k 时,先打上标记,表示该区间加上某一个数值。
如果想要查询某一段区间的值时将懒标记下传至儿子节点。
每一次递归时都要下传懒标记。
inline void push_down(ll k,ll l,ll r,ll mid)
{
//懒标记下传
lazy_tag[k<<1]=lazy_tag[k<<1]+lazy_tag[k];
sum[k<<1]=sum[k<<1]+lazy_tag[k]*(mid-l+1);
lazy_tag[k<<1|1]=lazy_tag[k<<1|1]+lazy_tag[k];
sum[k<<1|1]=sum[k<<1|1]+lazy_tag[k]*(r-(mid+1)+1);
lazy_tag[k]=0;//懒标记变为0
}
void add(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
if(l>=x&&r<=y)//完全包含
{
lazy_tag[k]+=ADD;//加入懒标记
sum[k]+=(ADD*(r-l+1));//对节点k的值进行修改
return;
}
else
{
ll mid=(l+r)>>1;
push_down(k,l,r,mid);//下传懒标记
if(x<=mid)add(k<<1,l,mid,x,y,ADD);
if(y>mid)add(k<<1|1,mid+1,r,x,y,ADD);
sum[k]=sum[k<<1]+sum[k<<1|1];//重新计算节点k的值
}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
if(l>=x&&r<=y)//完全包含则直接输出节点的值
{
return sum[k];
}
ll mid=(l+r)/2;
ll res=0;
push_down(k,l,r,mid);//下传懒标记
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
return res;
}
区间修改,区间查询(含乘法操作)
前提:求的是区间和对一个数字去模后的值。
加入两个懒标记 l a z y t a g 1 lazytag1 lazytag1 , l a z y t a g 2 lazytag2 lazytag2 。
l a z y t a g 2 lazytag2 lazytag2 的初始值为 1 1 1 。
l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] 表示对初始节点(标记下传后的节点 k k k ) 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] 。
l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k] 表示对初始节点(标记下传后的节点 k k k ) 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] 后加上 l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k] 。
标记下传时,儿子节点的值先乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] ,然后加上 l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k] 。
儿子节点的 l a z y t a g 2 lazytag2 lazytag2 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] 。
儿子节点的 l a z y t a g 1 lazytag1 lazytag1 乘上 l a z y t a g 2 [ k ] lazytag2[k] lazytag2[k] ,然后加上 l a z y t a g 1 [ k ] lazytag1[k] lazytag1[k] 。
注意,先乘后加!!!
inline void push_down(ll k,ll l,ll r,ll mid)
{
sum[k<<1]=(sum[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(mid-l+1))%mod;
sum[k<<1|1]=(sum[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(r-mid))%mod;
lazy_tag_cheng[k<<1]=(lazy_tag_cheng[k<<1]*lazy_tag_cheng[k])%mod;
lazy_tag_cheng[k<<1|1]=(lazy_tag_cheng[k<<1|1]*lazy_tag_cheng[k])%mod;
lazy_tag_jia[k<<1]=(lazy_tag_jia[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
lazy_tag_jia[k<<1|1]=(lazy_tag_jia[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
lazy_tag_jia[k]=0;
lazy_tag_cheng[k]=1;
}
void add_cheng(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
if(l>=x&&r<=y)
{
sum[k]*=ADD;
sum[k]%=mod;
lazy_tag_cheng[k]=(lazy_tag_cheng[k]*ADD)%mod;
lazy_tag_jia[k]=(lazy_tag_jia[k]*ADD)%mod;
return;
}
else
{
ll mid=(l+r)>>1;
push_down(k,l,r,mid);
if(x<=mid)add_cheng(k<<1,l,mid,x,y,ADD);
if(y>mid)add_cheng(k<<1|1,mid+1,r,x,y,ADD);
sum[k]=sum[k<<1]+sum[k<<1|1];sum[k]%=mod;
}
}
void add_jia(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
if(l>=x&&r<=y)
{
lazy_tag_jia[k]=(lazy_tag_jia[k]+ADD)%mod;
sum[k]+=(ADD*(r-l+1));
sum[k]%=mod;
return;
}
else
{
ll mid=(l+r)>>1;
push_down(k,l,r,mid);
if(x<=mid)add_jia(k<<1,l,mid,x,y,ADD);
if(y>mid)add_jia(k<<1|1,mid+1,r,x,y,ADD);
sum[k]=sum[k<<1]+sum[k<<1|1];
sum[k]%=mod;
}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
if(l>=x&&r<=y)
{
return sum[k];
}
ll mid=(l+r)/2;
ll res=0;
push_down(k,l,r,mid);
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
return res%mod;
}
例题
P3372 【模板】线段树 1
#include<bits/stdc++.h>
using namespace std;
#define ll long long
unsigned ll n,m;
unsigned ll a[1000001];
unsigned ll sum[4000001];
unsigned ll lazy_tag[4000001];
void build(ll k,ll l,ll r)
{
lazy_tag[k]=0;
if(l==r)
{
sum[k]=a[l];
return;
}
else
{
int mid=(l+r)/2;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
}
inline void push_down(ll k,ll l,ll r,ll mid)
{
lazy_tag[k<<1]=lazy_tag[k<<1]+lazy_tag[k];
sum[k<<1]=sum[k<<1]+lazy_tag[k]*(mid-l+1);
lazy_tag[k<<1|1]=lazy_tag[k<<1|1]+lazy_tag[k];
sum[k<<1|1]=sum[k<<1|1]+lazy_tag[k]*(r-(mid+1)+1);
lazy_tag[k]=0;
}
void add(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
if(l>=x&&r<=y)
{
lazy_tag[k]+=ADD;
sum[k]+=(ADD*(r-l+1));
return;
}
else
{
ll mid=(l+r)>>1;
push_down(k,l,r,mid);
if(x<=mid)add(k<<1,l,mid,x,y,ADD);
if(y>mid)add(k<<1|1,mid+1,r,x,y,ADD);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
if(l>=x&&r<=y)
{
return sum[k];
}
ll mid=(l+r)/2;
ll res=0;
push_down(k,l,r,mid);
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
return res;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1; i<=n; i++)
scanf("%lld",&a[i]);
build(1,1,n);
for(ll i=1; i<=m; i++)
{
ll op,x,y;
scanf("%lld%lld%lld",&op,&x,&y);
if(op==2)
{
printf("%lld\n",query(1,1,n,x,y));
}
else
{
ll k;
scanf("%lld",&k);
add(1,1,n,x,y,k);
}
}
}
P3373 【模板】线段树 2
#include<bits/stdc++.h>
using namespace std;
#define ll long long
unsigned ll n,m;
unsigned ll a[1000001];
unsigned ll sum[4000001];
unsigned ll lazy_tag_cheng[4000001];
unsigned ll lazy_tag_jia[4000001],mod;
void build(ll k,ll l,ll r)
{
lazy_tag_jia[k]=0;
lazy_tag_cheng[k]=1;
if(l==r)
{
sum[k]=a[l];
sum[k]%=mod;
return;
}
else
{
int mid=(l+r)/2;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
sum[k]%=mod;
}
}
inline void push_down(ll k,ll l,ll r,ll mid)
{
sum[k<<1]=(sum[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(mid-l+1))%mod;
sum[k<<1|1]=(sum[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k]*(r-mid))%mod;
lazy_tag_cheng[k<<1]=(lazy_tag_cheng[k<<1]*lazy_tag_cheng[k])%mod;
lazy_tag_cheng[k<<1|1]=(lazy_tag_cheng[k<<1|1]*lazy_tag_cheng[k])%mod;
lazy_tag_jia[k<<1]=(lazy_tag_jia[k<<1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
lazy_tag_jia[k<<1|1]=(lazy_tag_jia[k<<1|1]*lazy_tag_cheng[k]+lazy_tag_jia[k])%mod;
lazy_tag_jia[k]=0;
lazy_tag_cheng[k]=1;
}
void add_cheng(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
if(l>=x&&r<=y)
{
sum[k]*=ADD;
sum[k]%=mod;
lazy_tag_cheng[k]=(lazy_tag_cheng[k]*ADD)%mod;
lazy_tag_jia[k]=(lazy_tag_jia[k]*ADD)%mod;
return;
}
else
{
ll mid=(l+r)>>1;
push_down(k,l,r,mid);
if(x<=mid)add_cheng(k<<1,l,mid,x,y,ADD);
if(y>mid)add_cheng(k<<1|1,mid+1,r,x,y,ADD);
sum[k]=sum[k<<1]+sum[k<<1|1];sum[k]%=mod;
}
}
void add_jia(ll k,ll l,ll r,ll x,ll y,ll ADD)
{
if(l>=x&&r<=y)
{
lazy_tag_jia[k]=(lazy_tag_jia[k]+ADD)%mod;
sum[k]+=(ADD*(r-l+1));
sum[k]%=mod;
return;
}
else
{
ll mid=(l+r)>>1;
push_down(k,l,r,mid);
if(x<=mid)add_jia(k<<1,l,mid,x,y,ADD);
if(y>mid)add_jia(k<<1|1,mid+1,r,x,y,ADD);
sum[k]=sum[k<<1]+sum[k<<1|1];
sum[k]%=mod;
}
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
if(l>=x&&r<=y)
{
return sum[k];
}
ll mid=(l+r)/2;
ll res=0;
push_down(k,l,r,mid);
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
return res%mod;
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&mod);
for(ll i=1; i<=n; i++)
scanf("%lld",&a[i]);
build(1,1,n);
for(ll i=1; i<=m; i++)
{
ll op,x,y;
scanf("%lld%lld%lld",&op,&x,&y);
if(op==3)
{
printf("%lld\n",query(1,1,n,x,y));
}
else
if(op==1)
{
ll k;
scanf("%lld",&k);
add_cheng(1,1,n,x,y,k);
}
else
if(op==2)
{
ll k;scanf("%lld",&k);
add_jia(1,1,n,x,y,k);
}
}
}