线段树区间修改与查询
Posted bw-orzzzzz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树区间修改与查询相关的知识,希望对你有一定的参考价值。
单点修改与查询
//单点修改,区间询问最小值 #include <iostream> #include <cstdio> #define maxn 101 #define INF 0x7fffffff using namespace std; int a[maxn],n,m; int mi[maxn]; void build(int k,int l,int r)//k是当前节点编号,l,r为当前节点代表区间 { if(l==r) { mi[k]=a[l]; return; } int mid=(l+r)/2; build(k*2,l,mid);//左子树 build(k*2+1,mid+1,r);//右子树 mi[k]=min(mi[2*k],mi[2*k+1]);//维护最小值 }//建立线段树 int query(int k,int l,int r,int x,int y)//x,y为询问区间,l,r为当前区间 { if(y<l||x>r)return INF;//询问区间与当前区间完全无交集 if(x<=l&&y>=r) { return mi[k]; }//询问区间完全包含当前区间 int mid=(l+r)/2; return min(query(k*2,l,mid,x,y),query(k*2+1,mid+1,r,x,y));//否则分别处理左右子树 } void update(int k,int l,int r,int x,int v)//x为原序列位置,v为要修改的值 { if(r<x||l>x)return; //当前区间与原序列位置完全无交集 if(l==r&&l==x)//当前节点为叶子结点 { mi[k]=v;//修改叶子结点 return; } int mid=(l+r)/2; update(k*2,l,mid,x,v); update(k*2+1,mid+1,r,x,v); mi[k]=min(mi[k*2],mi[k*2+1]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } build(1,1,n); for(int i=1;i<=m;i++) { int k,l,r; scanf("%d%d%d",&k,&l,&r); if(k==0)//更新 { update(1,1,n,l,r); } if(k==1) { int ans=query(1,1,n,l,r); printf("%d\n",ans); } } return 0; }
区间修改与查询
注意要使用标记下传来实现。
//线段树-区间修改,查询区间和 #include <iostream> #include <cstdio> #define ll long long #define maxn 1000005 using namespace std; ll sum[maxn];//序号为k的区间的区间和 ll a[maxn]; ll add[maxn];//lazy标记 int n,m; void build(int k,int l,int r) { if(l==r) { sum[k]=a[l]; return; } int mid=(l+r)/2; build(2*k,l,mid); build(2*k+1,mid+1,r); sum[k]=sum[2*k]+sum[2*k+1]; }//建立线段树,维护区间和 void Add(int k,int l,int r,ll v)//给定区间[l,r]所有数加上v(打标记) { add[k]+=v; sum[k]+=(r-l+1)*v;//维护区间和 return; } void pushdown(int k,int l,int r,int mid)//标记下传 { if(add[k]==0)return; Add(k*2,l,mid,add[k]);//下传到左子树 Add(k*2+1,mid+1,r,add[k]);//下传到右子树 add[k]=0; //清零标记 } void modify(int k,int l,int r,int x,int y,ll v)//给区间[x,y]所有的数加上v(真的加上) { if(l>=x&&r<=y) { return Add(k,l,r,v); } int mid=(l+r)/2; pushdown(k,l,r,mid); //到达每一个结点都要下传标记 if(x<=mid)modify(2*k,l,mid,x,y,v);//修改左子树 if(y>mid)modify(2*k+1,mid+1,r,x,y,v);//修改右子树 sum[k]=sum[2*k]+sum[2*k+1]; } ll query(int k,int l,int r,int x,int y) { if(l>=x&&r<=y) { return sum[k]; } int mid=(l+r)/2; ll res=0; pushdown(k,l,r,mid); if(x<=mid) { res+=query(k*2,l,mid,x,y); } if(y>mid) { res+=query(k*2+1,mid+1,r,x,y); } return res; }//询问区间[x,y]的区间和 int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } build(1,1,n); for(int i=1;i<=m;i++) { int op,l,r; scanf("%d%d%d",&op,&l,&r); if(op==1) { ll v; scanf("%lld",&v); modify(1,1,n,l,r,v); } if(op==2) { ll ans=query(1,1,n,l,r); printf("%lld\n",ans); } } return 0; }
以上是关于线段树区间修改与查询的主要内容,如果未能解决你的问题,请参考以下文章