线段树区间修改与查询

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;
}

 

以上是关于线段树区间修改与查询的主要内容,如果未能解决你的问题,请参考以下文章

线段树区间修改与查询

树状数组与线段树

线段树详解

P3372 模板线段树 1(区间修改区间查询)(树状数组)

模板线段树-单点修改,区间查询

线段树 :区间修改,区间查询