线段树用于区间的修改统计问题,能用线段树维护的东西,必须满足区间可加性,即可用左右子结点推出父节点。
但其实线段树的用法远不止这些,这里来总结线段树的基本模板写法。
首先是递归建树 需要注意的地方是存树的数组要开4倍大小
push_up函数用来更新结点,这里以区间求和为例
inline void push_up(int k){
t[k]=t[k<<1]+t[k<<1|1];
}
inline void build(int l,int r,int root){
if(l==r){
t[root]=a[l]; // a[i] 是原数组
return ; //不要忘了return
}
int mid=(l+r)/2;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1); //位运算更快,相当于root*2+1
push_up(root); //更新结点
}
在输入初始a数组后调用build(1,n,1)就行了
然后就是单点修改操作
inline void add(int q,int v,int l,int r,int root){ // q为更新结点,v为更新值
if(l==r){
t[root]+=v;
return ;
}
int mid=(l+r)/2;
if(q<=mid){
add(q,v,l,mid,root<<1); //递归更新子结点
}
else {
add(q,v,mid+1,r,root<<1|1);
}
push_up(root); // 子结点更新后不要忘记更新父节点
}
然后就是区间查询操作
inline int query(int fl,int fr,int l,int r,int root){ //fl,fr为查询区间
if(fl<=l&&fr>=r){
return t[root]; //结点完全被包含在查询区间内
}
int mid=(l+r)/2;
int ans=0;
if(fl<=mid)ans+=query(fl,fr,l,mid,root<<1); //递归查询
if(fr>mid)ns+=query(fl,fr,mid+1,r,root<<1|1);
return ans;
}
可以写一写这个模板题 hdoj 1166
对于升级版的题目,需要进行区间修改,这个时候逐个进行单点修改肯定是不行的,就需要加入一个lazytag了,就是只有查询到当前区间才把标记下传,这样就大大减少了修改时间。
而核心操作就是push_down操作了
inline void push_down(int k){ //以区间加法为例
if(t[k].flag){
t[k<<1].flag+=t[k].flag; //继承父标记
t[k<<1|1].flag+=t[k].flag; //加法标记可加性
t[k<<1].val+=(t[k<<1].r-t[k<<1].l+1)*t[k].flag;
t[k<<1|1].val+=(t[k<<1|1].r-t[k<<1|1].l+1)*t[k].flag;
t[k].flag=0; //标记下穿后要清零
}
}
为了方便操作,也把数组改成了结构体
可以写一下洛谷的这个模板题洛谷P3327
下面附上AC代码,可以当作区间修改模板
#include <bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
typedef long long ll;
struct node {
ll val;
int l,r;
int flag;
}t[4*MAX];
int a[MAX];
inline void push_up(int k){
t[k].val=t[k<<1].val+t[k<<1|1].val;
return ;
}
inline void build(int l,int r,int root){
t[root].l=l;
t[root].r=r;
if(l==r){
t[root].val=a[l];
return ;
}
int mid=(l+r)/2;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
push_up(root);
}
inline void push_down(int k){
if(t[k].flag){
t[k<<1].flag+=t[k].flag;
t[k<<1|1].flag+=t[k].flag;
t[k<<1].val+=(t[k<<1].r-t[k<<1].l+1)*t[k].flag;
t[k<<1|1].val+=(t[k<<1|1].r-t[k<<1|1].l+1)*t[k].flag;
t[k].flag=0;
}
}
inline void add(int fl,int fr,int v,int l,int r,int root){
if(fl<=l&&fr>=r){
t[root].val+=(t[root].r-t[root].l+1)*v;
t[root].flag+=v;
return ;
}
push_down(root);
int mid=(l+r)/2;
if(fl<=mid) add(fl,fr,v,l,mid,root<<1);
if(fr>mid) add(fl,fr,v,mid+1,r,root<<1|1);
push_up(root);
}
inline ll query(int fl,int fr,int l,int r,int root){
ll ans=0;
if(fl<=l&&fr>=r){
return t[root].val;
}
push_down(root);
int mid=(l+r)/2;
if(fl<=mid) ans+=query(fl,fr,l,mid,root<<1);
if(fr>mid) ans+=query(fl,fr,mid+1,r,root<<1|1);
return ans;
}
int main (){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,n,1);
int a,x,y,z;
for(int i=0;i<m;i++){
scanf("%d",&a);
if(a==1){
cin>>x>>y>>z;
add(x,y,z,1,n,1);
}else {
cin>>x>>y;
printf("%lld\n",query(x,y,1,n,1));
}
}
return 0;
}
最后就是洛谷的线段树模板题二,乘法操作和加法操作同时存在,要注意先进行乘法操作,这要可以方便对加法标记进行修改,下面直接附上AC代码,可当作模板使用
#include <bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
int a[MAX];
int p;
typedef long long ll;
struct node{
ll val;
ll l,r;
ll flag;
ll flagm;
}t[4*MAX];
inline void push_up(int k){
t[k].val=(t[k<<1|1].val+t[k<<1].val)%p;
return ;
}
inline void build(int l,int r,int root){
t[root].l=l;
t[root].r=r;
t[root].flagm=1;
if(l==r){
t[root].val=a[l];
return ;
}
int mid=(l+r)/2;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
push_up(root);
}
inline void push_down(int k){
if(t[k].flagm!=1){
t[k<<1].val=t[k<<1].val*t[k].flagm%p;
t[k<<1|1].val=t[k<<1|1].val*t[k].flagm%p;
t[k<<1].flagm=t[k<<1].flagm*t[k].flagm%p;
t[k<<1|1].flagm=t[k<<1|1].flagm*t[k].flagm%p;
t[k<<1].flag=(t[k<<1].flag*t[k].flagm)%p;
t[k<<1|1].flag=(t[k<<1|1].flag*t[k].flagm)%p;
t[k].flagm=1;
}
if(t[k].flag){
t[k<<1].val=(t[k<<1].val+(t[k<<1].r-t[k<<1].l+1)*t[k].flag)%p;
t[k<<1|1].val=(t[k<<1|1].val+(t[k<<1|1].r-t[k<<1|1].l+1)*t[k].flag)%p;
t[k<<1].flag+=t[k].flag;
t[k<<1|1].flag+=t[k].flag;
t[k].flag=0;
}
}
inline void add(int fl,int fr,int k,int l,int r,int root){
if(fl<=l&&fr>=r){
t[root].val=(t[root].val+(t[root].r-t[root].l+1)*k)%p;
t[root].flag+=k;
return ;
}
push_down(root);
int mid=(l+r)/2;
if(fl<=mid) add(fl,fr,k,l,mid,root<<1);
if(mid<fr) add(fl,fr,k,mid+1,r,root<<1|1);
push_up(root);
}
inline void mut(int fl,int fr,int k,int l,int r,int root){
if(fl<=l&&fr>=r){
t[root].val=(t[root].val*k)%p;
t[root].flagm=(k*t[root].flagm)%p;
t[root].flag=(k*t[root].flag)%p;
return ;
}
push_down(root);
int mid=(l+r)/2;
if(fl<=mid) mut(fl,fr,k,l,mid,root<<1);
if(fr>mid) mut(fl,fr,k,mid+1,r,root<<1|1);
push_up(root);
}
inline ll query(int fl,int fr,int l,int r,int root){
ll ans=0;
if(fl<=l&&fr>=r){
ans=t[root].val;
return ans;
}
push_down(root);
int mid=(l+r)/2;
if(fl<=mid) ans+=query(fl,fr,l,mid,root<<1);
if(fr>mid) ans+=query(fl,fr,mid+1,r,root<<1|1);
return ans;
}
int main(){
int n,m;
cin>>n>>m>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
int op,x,y,k;
for(int i=0;i<m;i++){
cin>>op;
if(op==1){
cin>>x>>y>>k;
mut(x,y,k,1,n,1);
}
else if(op==2){
cin>>x>>y>>k;
add(x,y,k,1,n,1);
}
else {
cin>>x>>y;
cout<<query(x,y,1,n,1)%p<<endl;
}
}
return 0;
}