线段树1链接:https://www.luogu.org/problemnew/show/P3372
线段树2链接:https://www.luogu.org/problemnew/show/P3373
线段树是一种维护区间的数据结构,且满足二叉树的全部性质
下图是一棵维护区间 的线段树↓
格式:
我们可以发现,对于每个节点 来说,其左节点编号为 ,右节点编号为 ,左节点维护区间 ,右节点维护区间 这样我们就得到了递归建树的伪代码:
build(k,l,r)
if(l=r)
获取节点信息
return
i←2k,mid←(l+r)/2
build(i,l,mid)
build(i+1,mid+1,r)
合并子节点信息
end
然后我们考虑区间查询操作
比方说我要查询区间 的信息
我们只需要获取节点 与节点 的信息,就可以通过合并信息的方式获取到整块区间的信息
这一步也是可以递归求解的,只需要对维护区间判断即可,下面给出伪代码
ask(k,l,r,le,ri)
if(le<=l and r<=ri) return 节点k信息
i←2k,mid←(l+r)/2
sum←NULL
if(le<=mid) 合并sum,ask(i,l,mid,le,ri)
if(mid<ri) 合并sum,ask(i+1,mid+1,r,le,ri)
return sum信息
end
如果要进行区间修改呢?
我们仍以修改 为例
我们需要修改节点 与节点 的信息,并将修改计入上层节点的贡献
对于多个标记贡献与顺序有关的情况,我们采用标记下传的方式解决,下面是伪代码
po(k,l,r)
if(l=r or 当前节点没有标记) return
标记下传
清空当前层标记
end
cchg(k,l,r,le,ri)
po(k,l,r)
if(le<=l and r<=ri)
修改当前节点
return
i←2k,mid←(l+r)/2
cchg(i,l,mid,le,ri)
cchg(i+1,mid+1,r,le,ri)
合并下层节点信息
end
这样,我们就解决了序列区间修改与查询的问题
那么以线段树2为例,我们给出C++的代码↓
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1<<17;
int n,m,MOD,x,p,l,r;
int a[MAXN];
long long tree[MAXN<<1],add[MAXN<<1],mul[MAXN<<1];
void build(int k,int l,int r){
mul[k]=1;
if(l==r){
tree[k]=a[l];
return;
}int i=k<<1,mid=l+r>>1;
build(i,l,mid);
build(i|1,mid+1,r);
tree[k]=tree[i]+tree[i|1];
}
void po(int k,int l,int r){
if(l==r||add[k]==0&&mul[k]==1) return;
int i=k<<1,mid=l+r>>1;
mul[i]=(mul[i]*mul[k])%MOD;
mul[i|1]=(mul[i|1]*mul[k])%MOD;
add[i]=(add[i]*mul[k]+add[k])%MOD;
add[i|1]=(add[i|1]*mul[k]+add[k])%MOD;
tree[i]=(tree[i]*mul[k]+add[k]*(mid-l+1))%MOD;
tree[i|1]=(tree[i|1]*mul[k]+add[k]*(r-mid))%MOD;
add[k]=0,mul[k]=1;
}
void cadd(int k,int l,int r,int le,int ri,int x){
po(k,l,r);
if(le<=l&&r<=ri){
add[k]=x;
tree[k]=(tree[k]+x*(r-l+1))%MOD;
return;
}int i=k<<1,mid=l+r>>1;
if(le<=mid) cadd(i,l,mid,le,ri,x);
if(mid<ri) cadd(i|1,mid+1,r,le,ri,x);
tree[k]=(tree[i]+tree[i|1])%MOD;
}
void cmul(int k,int l,int r,int le,int ri,int x){
po(k,l,r);
if(le<=l&&r<=ri){
mul[k]=x;
tree[k]=(tree[k]*x)%MOD;
return;
}int i=k<<1,mid=l+r>>1;
if(le<=mid) cmul(i,l,mid,le,ri,x);
if(mid<ri) cmul(i|1,mid+1,r,le,ri,x);
tree[k]=(tree[i]+tree[i|1])%MOD;
}
long long ask(int k,int l,int r,int le,int ri){
po(k,l,r);
if(le<=l&&r<=ri) return tree[k]%MOD;
int i=k<<1,mid=l+r>>1;
long long sum=0;
if(le<=mid) sum+=ask(i,l,mid,le,ri);
if(mid<ri) sum+=ask(i|1,mid+1,r,le,ri);
return sum%MOD;
}
void init(){
scanf("%d%d%d",&n,&m,&MOD);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
build(1,1,n);
return;
}
void solve(){
while(m--){
scanf("%d",&p);
if(p==1) scanf("%d%d%d",&l,&r,&x),cmul(1,1,n,l,r,x);
else if(p==2) scanf("%d%d%d",&l,&r,&x),cadd(1,1,n,l,r,x);
else scanf("%d%d",&l,&r),printf("%lld\n",ask(1,1,n,l,r));
}return;
}
int main(){
init();
solve();
return 0;
}