线段树
线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。
功能
单点更新
建树
如果父节点的下标为id,那么左儿子下标为2*id,右儿子下标为2*id+1。
void build(int id,int l,int r)
{
if(l==r) //左端点与右端点相等
{
tr[id]=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid); //左子树
build(id*2+1,mid+1,r);//右子树
//更新父节点
tr[id]=min(tr[id*2],tr[id*2+1]);
}
值的更新
//单点更新
//id 树结点编号,l,r为其对应的区间,将下标为x的元素修改为v
void genxin(int id,int l,int r,int x,int v)
{
//区间长度为1,也就是左端点等于右端点 找到了
if(l==r)
{
tr[id]=v;
return ;
}
//否则 找到x所在的区间
int mid=(l+r)/2;
//在左区间
if(x<=mid) genxin(id*2,l,mid,x,v);
//右区间
else genxin(id*2+1,mid+1,r,x,v);
tr[id]=min(tr[id*2],tr[id*2+1]); //查询最小值
}
区间查询
int find(int id,int l,int r,int x,int y)
{
if(x>r||y<l) return 0;
//该区间被包含在要查询的区间内
if(x<=l&&r<=y) return tr[id];
int mid=(l+r)/2;
int ans=INT_MAX;
//与左半区间有交集
if(x<=mid) ans=min(ans,find(id*2,l,mid,x,y)); //看求的是区间内的最小值还是最大值
//与右半区间有交集
else ans=min(ans,find(id*2+1,mid+1,r,x,y));
return ans;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
int tr[N];
int a[N];
//建树
void build(int id,int l,int r)
{
if(l==r) //左端点与右端点相等
{
tr[id]=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid); //左子树
build(id*2+1,mid+1,r);//右子树
//更新父节点
tr[id]=min(tr[id*2],tr[id*2+1]);
}
//单点更新
//id 树结点编号,l,r为其对应的区间,将下标为x的元素修改为v
void genxin(int id,int l,int r,int x,int v)
{
//区间长度为1,也就是左端点等于右端点 找到了
if(l==r)
{
tr[id]=v;
return ;
}
//否则 找到x所在的区间
int mid=(l+r)/2;
//在左区间
if(x<=mid) genxin(id*2,l,mid,x,v);
//右区间
else genxin(id*2+1,mid+1,r,x,v);
tr[id]=min(tr[id*2],tr[id*2+1]);
}
//区间查询 l,r为当前区间,x,y为要查询的区间 id树结点编号
int find(int id,int l,int r,int x,int y)
{
if(x>r||y<l) return 0;
//该区间被包含在要查询的区间内
if(x<=l&&r<=y) return tr[id];
int mid=(l+r)/2;
int ans=INT_MAX;
//与左半区间有交集
if(x<=mid) ans=min(ans,find(id*2,l,mid,x,y)); //看求的是区间内的最小值还是最大值
//与右半区间有交集
else ans=min(ans,find(id*2+1,mid+1,r,x,y));
return ans;
}
int main()
{
int m,n;
cin>>m>>n;
for(int i=1;i<=m;i++)
cin>>a[i];
build(1,1,m);
while(n--)
{
int p,x,y;
cin>>p>>x>>y;
if(p==1)
cout<<find(1,1,m,x,y)<<' ';
else
genxin(1,1,m,x,y);
}
return 0;
}
区间更新
建树
void build(int id,int l,int r)
{
if(l==r) //左端点与右端点相等
{
sum[id]=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid); //左子树
build(id*2+1,mid+1,r);//右子树
//更新父节点
push_up(id);
}
维护区间和
void push_up(int id)
{
sum[id]=sum[id*2]+sum[id*2+1];//维护区间和
}
懒标记
void push_down(int id,int l,int r)
{
int mid=(l+r)/2;
lazy[id*2]+=lazy[id]; //子节点进行标记
lazy[id*2+1]+=lazy[id];
sum[id*2]+=lazy[id]*(mid-l+1); //子节点的值发生变化
sum[id*2+1]+=lazy[id]*(r-mid);
lazy[id]=0;//清空lazy标记
}
区间更新
//区间更新
void qjgx(int id,int l,int r,int x,int y,int v)
{
if(x>r||y<l) return ;
//区间包含在被查找的区间
if(l>=x&&r<=y)
{
lazy[id]+=v;
sum[id]+=v*(r-l+1);
return ;
}
push_down(id,l,r);//更新下面的节点
int mid=(l+r)/2;
//左半区间
qjgx(id*2,l,mid,x,y,v);
//右半区间
qjgx(id*2+1,mid+1,r,x,y,v);
push_up(id);//子节点更新完成后,父节点也要更新
}
区间查询
//区间查询 l,r为当前区间,x,y为要查询的区间 id树结点编号
int find(int id,int l,int r,int x,int y)
{
if(x>r||y<l) return 0;
//该区间被包含在要查询的区间内
if(x<=l&&r<=y) return sum[id];
push_down(id,l,r);//要查子节点了,下放
int mid=(l+r)/2;
return find(id*2,l,mid,x,y)+find(id*2+1,mid+1,r,x,y);
}
模板1
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int sum[10000001],a[10000001],lazy[10000001];
void push_up(int id)
{
sum[id]=sum[id*2]+sum[id*2+1];//维护区间和
}
void push_down(int id,int l,int r)
{
int mid=(l+r)/2;
lazy[id*2]+=lazy[id]; //子节点进行标记
lazy[id*2+1]+=lazy[id];
sum[id*2]+=lazy[id]*(mid-l+1); //子节点的值发生变化
sum[id*2+1]+=lazy[id]*(r-mid);
lazy[id]=0;//清空lazy标记
}
//建树
void build(int id,int l,int r)
{
if(l==r) //左端点与右端点相等
{
sum[id]=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid); //左子树
build(id*2+1,mid+1,r);//右子树
//更新父节点
push_up(id);
}
//区间更新
void qjgx(int id,int l,int r,int x,int y,int v)
{
if(x>r||y<l) return ;
//区间包含在被查找的区间
if(l>=x&&r<=y)
{
lazy[id]+=v;
sum[id]+=v*(r-l+1);
return ;
}
push_down(id,l,r);//更新下面的节点
int mid=(l+r)/2;
qjgx(id*2,l,mid,x,y,v);
qjgx(id*2+1,mid+1,r,x,y,v);
push_up(id);//子节点更新完成后,父节点也要更新
}
//区间查询 l,r为当前区间,x,y为要查询的区间 id树结点编号
int find(int id,int l,int r,int x,int y)
{
if(x>r||y<l) return 0;
//该区间被包含在要查询的区间内
if(x<=l&&r<=y) return sum[id];
push_down(id,l,r);//要查子节点了,下放
int mid=(l+r)/2;
int ans=0;
return find(id*2,l,mid,x,y)+find(id*2+1,mid+1,r,x,y);
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n); //先建树
while(m--)
{
int p,x,y,k;
cin>>p>>x>>y;
if(p==1)
{
cin>>k;
qjgx(1,1,n,x,y,k);
}
else
cout<<find(1,1,n,x,y)<<endl;
}
return 0;
}
模板2
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
long long mod;
long long a[N];
struct s
{
long long v,mul,add;
}st[N];
void build(int id,int l,int r)
{
st[id].mul=1;
st[id].add=0;
if(l==r)
{
st[id].v=a[l];
st[id].v%=mod;
return ;
}
else
{
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
st[id].v=st[id*2].v+st[id*2+1].v;
}
}
void push_down(int id,int l,int r)
{
int mid=(l+r)/2;
st[id*2].v=(st[id*2].v*st[id].mul+st[id].add*(mid-l+1))%mod;
st[id*2+1].v=(st[id*2+1].v*st[id].mul+st[id].add*(r-mid))%mod;
st[id*2].mul=(st[id*2].mul*st[id].mul)%mod;
st[id*2+1].mul=(st[id*2+1].mul*st[id].mul)%mod;
st[id*2].add=(st[id*2].add*st[id].mul+st[id].add)%mod;
st[id*2+1].add=(st[id*2+1].add*st[id].mul+st[id].add)%mod;
st[id].mul=1;
st[id].add=0;
return ;
}
void gx1(int id,int l,int r,int x,int y,int k)
{
if(x>r||y<l) return;
if(x<=l&&y>=r)
{
st[id].v=(st[id].v*k)%mod;
st[id].mul=(st[id].mul*k)%mod;
st[id].add=(st[id].add*k)%mod;
return ;
}
push_down(id,l,r);
int mid=(l+r)/2;
gx1(id*2,l,mid,x,y,k);
gx1(id*2+1,mid+1,r,x,y,k);
st[id].v=(st[id*2].v+st[id*2+1].v)%mod;
return ;
}
void gx2(int id,int l,int r,int x,int y,int k)
{
if(x>r||y<l) return;
if(x<=l&&y>=r)
{
st[id].add=(st[id].add+k)%mod;
st[id].v=(st[id].v+k*(r-l+1))%mod;
return ;
}
push_down(id,l,r);
int mid=(l+r)/2;
gx2(id*2,l,mid,x,y,k);
gx2(id*2+1,mid+1,r,x,y,k);
st[id].v=(st[id*2].v+st[id*2+1].v)%mod;
return ;
}
long long find(int id,int l,int r,int x,int y)
{
if(x>r||y<l) return 0;
if(x<=l&&y>=r)
{
return st[id].v;
}
push_down(id,l,r);
int mid=(l+r)/2;
return (find(id*2,l,mid,x,y)+find(id*2+1,mid+1,r,x,y))%mod;
}
int main()
{
int n,q;
cin>>n>>q>>mod;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
while(q--)
{
//cout<<q<<endl;
long long p,x,y,k;
cin>>p;
if(p==1)
{
cin>>x>>y>>k;
gx1(1,1,n,x,y,k);
}
else if(p==2)
{
cin>>x>>y>>k;
gx2(1,1,n,x,y,k);
}
else if(p==3)
{
cin>>x>>y;
cout<<find(1,1,n,x,y)<<endl;
}
}
return 0;
}